@codemirror/view 6.26.0 → 6.26.1
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 +68 -33
- package/dist/index.js +68 -33
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
## 6.26.1 (2024-03-28)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix the editor getting stuck in composition when Safari fails to fire a compositionend event for a dead key composition.
|
|
6
|
+
|
|
7
|
+
Fix an issue where, with IME systems that kept the cursor at the start of the composed text, the editor misidentified the target node and disrupted composition.
|
|
8
|
+
|
|
9
|
+
Fix a bug where in a line-wrapped editor, with some content, the initial scroll position would be off from the top of the document.
|
|
10
|
+
|
|
1
11
|
## 6.26.0 (2024-03-14)
|
|
2
12
|
|
|
3
13
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -61,6 +61,9 @@ function domIndex(node) {
|
|
|
61
61
|
return index;
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
+
function isBlockElement(node) {
|
|
65
|
+
return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
|
|
66
|
+
}
|
|
64
67
|
function scanFor(node, off, targetNode, targetOff, dir) {
|
|
65
68
|
for (;;) {
|
|
66
69
|
if (node == targetNode && off == targetOff)
|
|
@@ -342,6 +345,46 @@ function atElementStart(doc, selection) {
|
|
|
342
345
|
function isScrolledToBottom(elt) {
|
|
343
346
|
return elt.scrollTop > Math.max(1, elt.scrollHeight - elt.clientHeight - 4);
|
|
344
347
|
}
|
|
348
|
+
function textNodeBefore(startNode, startOffset) {
|
|
349
|
+
for (let node = startNode, offset = startOffset;;) {
|
|
350
|
+
if (node.nodeType == 3 && offset > 0) {
|
|
351
|
+
return { node: node, offset: offset };
|
|
352
|
+
}
|
|
353
|
+
else if (node.nodeType == 1 && offset > 0) {
|
|
354
|
+
if (node.contentEditable == "false")
|
|
355
|
+
return null;
|
|
356
|
+
node = node.childNodes[offset - 1];
|
|
357
|
+
offset = maxOffset(node);
|
|
358
|
+
}
|
|
359
|
+
else if (node.parentNode && !isBlockElement(node)) {
|
|
360
|
+
offset = domIndex(node);
|
|
361
|
+
node = node.parentNode;
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
function textNodeAfter(startNode, startOffset) {
|
|
369
|
+
for (let node = startNode, offset = startOffset;;) {
|
|
370
|
+
if (node.nodeType == 3 && offset < node.nodeValue.length) {
|
|
371
|
+
return { node: node, offset: offset };
|
|
372
|
+
}
|
|
373
|
+
else if (node.nodeType == 1 && offset < node.childNodes.length) {
|
|
374
|
+
if (node.contentEditable == "false")
|
|
375
|
+
return null;
|
|
376
|
+
node = node.childNodes[offset];
|
|
377
|
+
offset = 0;
|
|
378
|
+
}
|
|
379
|
+
else if (node.parentNode && !isBlockElement(node)) {
|
|
380
|
+
offset = domIndex(node) + 1;
|
|
381
|
+
node = node.parentNode;
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
345
388
|
|
|
346
389
|
class DOMPos {
|
|
347
390
|
constructor(node, offset, precise = true) {
|
|
@@ -2666,6 +2709,7 @@ class DocView extends ContentView {
|
|
|
2666
2709
|
this.hasComposition = null;
|
|
2667
2710
|
this.markedForComposition = new Set;
|
|
2668
2711
|
this.compositionBarrier = Decoration.none;
|
|
2712
|
+
this.lastCompositionAfterCursor = false;
|
|
2669
2713
|
// Track a minimum width for the editor. When measuring sizes in
|
|
2670
2714
|
// measureVisibleLineHeights, this is updated to point at the width
|
|
2671
2715
|
// of a given element and its extent in the document. When a change
|
|
@@ -2882,7 +2926,7 @@ class DocView extends ContentView {
|
|
|
2882
2926
|
if (browser.gecko) {
|
|
2883
2927
|
let nextTo = nextToUneditable(anchor.node, anchor.offset);
|
|
2884
2928
|
if (nextTo && nextTo != (1 /* NextTo.Before */ | 2 /* NextTo.After */)) {
|
|
2885
|
-
let text =
|
|
2929
|
+
let text = (nextTo == 1 /* NextTo.Before */ ? textNodeBefore : textNodeAfter)(anchor.node, anchor.offset);
|
|
2886
2930
|
if (text)
|
|
2887
2931
|
anchor = new DOMPos(text.node, text.offset);
|
|
2888
2932
|
}
|
|
@@ -3250,7 +3294,23 @@ class BlockGapWidget extends WidgetType {
|
|
|
3250
3294
|
}
|
|
3251
3295
|
function findCompositionNode(view, headPos) {
|
|
3252
3296
|
let sel = view.observer.selectionRange;
|
|
3253
|
-
|
|
3297
|
+
if (!sel.focusNode)
|
|
3298
|
+
return null;
|
|
3299
|
+
let textBefore = textNodeBefore(sel.focusNode, sel.focusOffset);
|
|
3300
|
+
let textAfter = textNodeAfter(sel.focusNode, sel.focusOffset);
|
|
3301
|
+
let textNode = textBefore || textAfter;
|
|
3302
|
+
if (textAfter && textBefore && textAfter.node != textBefore.node) {
|
|
3303
|
+
let descAfter = ContentView.get(textAfter.node);
|
|
3304
|
+
if (!descAfter || descAfter instanceof TextView && descAfter.text != textAfter.node.nodeValue) {
|
|
3305
|
+
textNode = textAfter;
|
|
3306
|
+
}
|
|
3307
|
+
else if (view.docView.lastCompositionAfterCursor) {
|
|
3308
|
+
let descBefore = ContentView.get(textBefore.node);
|
|
3309
|
+
if (!(!descBefore || descBefore instanceof TextView && descBefore.text != textBefore.node.nodeValue))
|
|
3310
|
+
textNode = textAfter;
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
view.docView.lastCompositionAfterCursor = textNode != textBefore;
|
|
3254
3314
|
if (!textNode)
|
|
3255
3315
|
return null;
|
|
3256
3316
|
let from = headPos - textNode.offset;
|
|
@@ -3285,33 +3345,6 @@ function findCompositionRange(view, changes, headPos) {
|
|
|
3285
3345
|
return null;
|
|
3286
3346
|
}
|
|
3287
3347
|
}
|
|
3288
|
-
function nearbyTextNode(startNode, startOffset, side) {
|
|
3289
|
-
if (side <= 0)
|
|
3290
|
-
for (let node = startNode, offset = startOffset;;) {
|
|
3291
|
-
if (node.nodeType == 3)
|
|
3292
|
-
return { node: node, offset: offset };
|
|
3293
|
-
if (node.nodeType == 1 && offset > 0) {
|
|
3294
|
-
node = node.childNodes[offset - 1];
|
|
3295
|
-
offset = maxOffset(node);
|
|
3296
|
-
}
|
|
3297
|
-
else {
|
|
3298
|
-
break;
|
|
3299
|
-
}
|
|
3300
|
-
}
|
|
3301
|
-
if (side >= 0)
|
|
3302
|
-
for (let node = startNode, offset = startOffset;;) {
|
|
3303
|
-
if (node.nodeType == 3)
|
|
3304
|
-
return { node: node, offset: offset };
|
|
3305
|
-
if (node.nodeType == 1 && offset < node.childNodes.length && side >= 0) {
|
|
3306
|
-
node = node.childNodes[offset];
|
|
3307
|
-
offset = 0;
|
|
3308
|
-
}
|
|
3309
|
-
else {
|
|
3310
|
-
break;
|
|
3311
|
-
}
|
|
3312
|
-
}
|
|
3313
|
-
return null;
|
|
3314
|
-
}
|
|
3315
3348
|
function nextToUneditable(node, offset) {
|
|
3316
3349
|
if (node.nodeType != 1)
|
|
3317
3350
|
return 0;
|
|
@@ -4540,6 +4573,10 @@ handlers.beforeinput = (view, event) => {
|
|
|
4540
4573
|
// keyboard.
|
|
4541
4574
|
view.observer.flushSoon();
|
|
4542
4575
|
}
|
|
4576
|
+
// Safari will occasionally forget to fire compositionend at the end of a dead-key composition
|
|
4577
|
+
if (browser.safari && event.inputType == "insertText" && view.inputState.composing >= 0) {
|
|
4578
|
+
setTimeout(() => observers.compositionend(view, event), 20);
|
|
4579
|
+
}
|
|
4543
4580
|
return false;
|
|
4544
4581
|
};
|
|
4545
4582
|
const appliedFirefoxHack = new Set;
|
|
@@ -4902,7 +4939,8 @@ class HeightMapGap extends HeightMap {
|
|
|
4902
4939
|
blockAt(height, oracle, top, offset) {
|
|
4903
4940
|
let { firstLine, lastLine, perLine, perChar } = this.heightMetrics(oracle, offset);
|
|
4904
4941
|
if (oracle.lineWrapping) {
|
|
4905
|
-
let guess = offset +
|
|
4942
|
+
let guess = offset + (height < oracle.lineHeight ? 0
|
|
4943
|
+
: Math.round(Math.max(0, Math.min(1, (height - top) / this.height)) * this.length));
|
|
4906
4944
|
let line = oracle.doc.lineAt(guess), lineHeight = perLine + line.length * perChar;
|
|
4907
4945
|
let lineTop = Math.max(top, height - lineHeight / 2);
|
|
4908
4946
|
return new BlockInfo(line.from, line.length, lineTop, lineHeight, 0);
|
|
@@ -6238,9 +6276,6 @@ function isAtEnd(parent, node, offset) {
|
|
|
6238
6276
|
node = node.parentNode;
|
|
6239
6277
|
}
|
|
6240
6278
|
}
|
|
6241
|
-
function isBlockElement(node) {
|
|
6242
|
-
return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
|
|
6243
|
-
}
|
|
6244
6279
|
class DOMPoint {
|
|
6245
6280
|
constructor(node, offset) {
|
|
6246
6281
|
this.node = node;
|
package/dist/index.js
CHANGED
|
@@ -59,6 +59,9 @@ function domIndex(node) {
|
|
|
59
59
|
return index;
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
+
function isBlockElement(node) {
|
|
63
|
+
return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
|
|
64
|
+
}
|
|
62
65
|
function scanFor(node, off, targetNode, targetOff, dir) {
|
|
63
66
|
for (;;) {
|
|
64
67
|
if (node == targetNode && off == targetOff)
|
|
@@ -340,6 +343,46 @@ function atElementStart(doc, selection) {
|
|
|
340
343
|
function isScrolledToBottom(elt) {
|
|
341
344
|
return elt.scrollTop > Math.max(1, elt.scrollHeight - elt.clientHeight - 4);
|
|
342
345
|
}
|
|
346
|
+
function textNodeBefore(startNode, startOffset) {
|
|
347
|
+
for (let node = startNode, offset = startOffset;;) {
|
|
348
|
+
if (node.nodeType == 3 && offset > 0) {
|
|
349
|
+
return { node: node, offset: offset };
|
|
350
|
+
}
|
|
351
|
+
else if (node.nodeType == 1 && offset > 0) {
|
|
352
|
+
if (node.contentEditable == "false")
|
|
353
|
+
return null;
|
|
354
|
+
node = node.childNodes[offset - 1];
|
|
355
|
+
offset = maxOffset(node);
|
|
356
|
+
}
|
|
357
|
+
else if (node.parentNode && !isBlockElement(node)) {
|
|
358
|
+
offset = domIndex(node);
|
|
359
|
+
node = node.parentNode;
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function textNodeAfter(startNode, startOffset) {
|
|
367
|
+
for (let node = startNode, offset = startOffset;;) {
|
|
368
|
+
if (node.nodeType == 3 && offset < node.nodeValue.length) {
|
|
369
|
+
return { node: node, offset: offset };
|
|
370
|
+
}
|
|
371
|
+
else if (node.nodeType == 1 && offset < node.childNodes.length) {
|
|
372
|
+
if (node.contentEditable == "false")
|
|
373
|
+
return null;
|
|
374
|
+
node = node.childNodes[offset];
|
|
375
|
+
offset = 0;
|
|
376
|
+
}
|
|
377
|
+
else if (node.parentNode && !isBlockElement(node)) {
|
|
378
|
+
offset = domIndex(node) + 1;
|
|
379
|
+
node = node.parentNode;
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
343
386
|
|
|
344
387
|
class DOMPos {
|
|
345
388
|
constructor(node, offset, precise = true) {
|
|
@@ -2662,6 +2705,7 @@ class DocView extends ContentView {
|
|
|
2662
2705
|
this.hasComposition = null;
|
|
2663
2706
|
this.markedForComposition = new Set;
|
|
2664
2707
|
this.compositionBarrier = Decoration.none;
|
|
2708
|
+
this.lastCompositionAfterCursor = false;
|
|
2665
2709
|
// Track a minimum width for the editor. When measuring sizes in
|
|
2666
2710
|
// measureVisibleLineHeights, this is updated to point at the width
|
|
2667
2711
|
// of a given element and its extent in the document. When a change
|
|
@@ -2878,7 +2922,7 @@ class DocView extends ContentView {
|
|
|
2878
2922
|
if (browser.gecko) {
|
|
2879
2923
|
let nextTo = nextToUneditable(anchor.node, anchor.offset);
|
|
2880
2924
|
if (nextTo && nextTo != (1 /* NextTo.Before */ | 2 /* NextTo.After */)) {
|
|
2881
|
-
let text =
|
|
2925
|
+
let text = (nextTo == 1 /* NextTo.Before */ ? textNodeBefore : textNodeAfter)(anchor.node, anchor.offset);
|
|
2882
2926
|
if (text)
|
|
2883
2927
|
anchor = new DOMPos(text.node, text.offset);
|
|
2884
2928
|
}
|
|
@@ -3246,7 +3290,23 @@ class BlockGapWidget extends WidgetType {
|
|
|
3246
3290
|
}
|
|
3247
3291
|
function findCompositionNode(view, headPos) {
|
|
3248
3292
|
let sel = view.observer.selectionRange;
|
|
3249
|
-
|
|
3293
|
+
if (!sel.focusNode)
|
|
3294
|
+
return null;
|
|
3295
|
+
let textBefore = textNodeBefore(sel.focusNode, sel.focusOffset);
|
|
3296
|
+
let textAfter = textNodeAfter(sel.focusNode, sel.focusOffset);
|
|
3297
|
+
let textNode = textBefore || textAfter;
|
|
3298
|
+
if (textAfter && textBefore && textAfter.node != textBefore.node) {
|
|
3299
|
+
let descAfter = ContentView.get(textAfter.node);
|
|
3300
|
+
if (!descAfter || descAfter instanceof TextView && descAfter.text != textAfter.node.nodeValue) {
|
|
3301
|
+
textNode = textAfter;
|
|
3302
|
+
}
|
|
3303
|
+
else if (view.docView.lastCompositionAfterCursor) {
|
|
3304
|
+
let descBefore = ContentView.get(textBefore.node);
|
|
3305
|
+
if (!(!descBefore || descBefore instanceof TextView && descBefore.text != textBefore.node.nodeValue))
|
|
3306
|
+
textNode = textAfter;
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
view.docView.lastCompositionAfterCursor = textNode != textBefore;
|
|
3250
3310
|
if (!textNode)
|
|
3251
3311
|
return null;
|
|
3252
3312
|
let from = headPos - textNode.offset;
|
|
@@ -3281,33 +3341,6 @@ function findCompositionRange(view, changes, headPos) {
|
|
|
3281
3341
|
return null;
|
|
3282
3342
|
}
|
|
3283
3343
|
}
|
|
3284
|
-
function nearbyTextNode(startNode, startOffset, side) {
|
|
3285
|
-
if (side <= 0)
|
|
3286
|
-
for (let node = startNode, offset = startOffset;;) {
|
|
3287
|
-
if (node.nodeType == 3)
|
|
3288
|
-
return { node: node, offset: offset };
|
|
3289
|
-
if (node.nodeType == 1 && offset > 0) {
|
|
3290
|
-
node = node.childNodes[offset - 1];
|
|
3291
|
-
offset = maxOffset(node);
|
|
3292
|
-
}
|
|
3293
|
-
else {
|
|
3294
|
-
break;
|
|
3295
|
-
}
|
|
3296
|
-
}
|
|
3297
|
-
if (side >= 0)
|
|
3298
|
-
for (let node = startNode, offset = startOffset;;) {
|
|
3299
|
-
if (node.nodeType == 3)
|
|
3300
|
-
return { node: node, offset: offset };
|
|
3301
|
-
if (node.nodeType == 1 && offset < node.childNodes.length && side >= 0) {
|
|
3302
|
-
node = node.childNodes[offset];
|
|
3303
|
-
offset = 0;
|
|
3304
|
-
}
|
|
3305
|
-
else {
|
|
3306
|
-
break;
|
|
3307
|
-
}
|
|
3308
|
-
}
|
|
3309
|
-
return null;
|
|
3310
|
-
}
|
|
3311
3344
|
function nextToUneditable(node, offset) {
|
|
3312
3345
|
if (node.nodeType != 1)
|
|
3313
3346
|
return 0;
|
|
@@ -4536,6 +4569,10 @@ handlers.beforeinput = (view, event) => {
|
|
|
4536
4569
|
// keyboard.
|
|
4537
4570
|
view.observer.flushSoon();
|
|
4538
4571
|
}
|
|
4572
|
+
// Safari will occasionally forget to fire compositionend at the end of a dead-key composition
|
|
4573
|
+
if (browser.safari && event.inputType == "insertText" && view.inputState.composing >= 0) {
|
|
4574
|
+
setTimeout(() => observers.compositionend(view, event), 20);
|
|
4575
|
+
}
|
|
4539
4576
|
return false;
|
|
4540
4577
|
};
|
|
4541
4578
|
const appliedFirefoxHack = /*@__PURE__*/new Set;
|
|
@@ -4897,7 +4934,8 @@ class HeightMapGap extends HeightMap {
|
|
|
4897
4934
|
blockAt(height, oracle, top, offset) {
|
|
4898
4935
|
let { firstLine, lastLine, perLine, perChar } = this.heightMetrics(oracle, offset);
|
|
4899
4936
|
if (oracle.lineWrapping) {
|
|
4900
|
-
let guess = offset +
|
|
4937
|
+
let guess = offset + (height < oracle.lineHeight ? 0
|
|
4938
|
+
: Math.round(Math.max(0, Math.min(1, (height - top) / this.height)) * this.length));
|
|
4901
4939
|
let line = oracle.doc.lineAt(guess), lineHeight = perLine + line.length * perChar;
|
|
4902
4940
|
let lineTop = Math.max(top, height - lineHeight / 2);
|
|
4903
4941
|
return new BlockInfo(line.from, line.length, lineTop, lineHeight, 0);
|
|
@@ -6233,9 +6271,6 @@ function isAtEnd(parent, node, offset) {
|
|
|
6233
6271
|
node = node.parentNode;
|
|
6234
6272
|
}
|
|
6235
6273
|
}
|
|
6236
|
-
function isBlockElement(node) {
|
|
6237
|
-
return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
|
|
6238
|
-
}
|
|
6239
6274
|
class DOMPoint {
|
|
6240
6275
|
constructor(node, offset) {
|
|
6241
6276
|
this.node = node;
|