prosemirror-transform 1.10.5 → 1.12.0
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 +16 -0
- package/dist/index.cjs +38 -4
- package/dist/index.d.cts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +52 -1
- package/package.json +1 -1
- package/src/replace.ts +16 -0
- package/src/replace_step.ts +10 -1
- package/src/transform.ts +21 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## 1.12.0 (2026-03-30)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Handle deletions that go from the beginning of one textblock to the beginning of another specially, deleting the selected textblocks entirely.
|
|
6
|
+
|
|
7
|
+
### New features
|
|
8
|
+
|
|
9
|
+
`ReplaceStep.MAP_BIAS` can now be adjusted to change the way insertion steps are mapped over other insertions at the same place. The default behavior remains the same for the sake of backwards compatibility.
|
|
10
|
+
|
|
11
|
+
## 1.11.0 (2026-01-21)
|
|
12
|
+
|
|
13
|
+
### New features
|
|
14
|
+
|
|
15
|
+
The new `Tranform.changedRange` method gives you a document range that includes all changes made by the transform/transaction.
|
|
16
|
+
|
|
1
17
|
## 1.10.5 (2025-11-11)
|
|
2
18
|
|
|
3
19
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -610,8 +610,8 @@ var ReplaceStep = function (_Step5) {
|
|
|
610
610
|
}, {
|
|
611
611
|
key: "map",
|
|
612
612
|
value: function map(mapping) {
|
|
613
|
-
var
|
|
614
|
-
|
|
613
|
+
var to = mapping.mapResult(this.to, -1);
|
|
614
|
+
var from = this.from == this.to && ReplaceStep.MAP_BIAS < 0 ? to : mapping.mapResult(this.from, 1);
|
|
615
615
|
if (from.deletedAcross && to.deletedAcross) return null;
|
|
616
616
|
return new ReplaceStep(from.pos, Math.max(from.pos, to.pos), this.slice, this.structure);
|
|
617
617
|
}
|
|
@@ -650,6 +650,7 @@ var ReplaceStep = function (_Step5) {
|
|
|
650
650
|
}]);
|
|
651
651
|
return ReplaceStep;
|
|
652
652
|
}(Step);
|
|
653
|
+
ReplaceStep.MAP_BIAS = 1;
|
|
653
654
|
Step.jsonID("replace", ReplaceStep);
|
|
654
655
|
var ReplaceAroundStep = function (_Step6) {
|
|
655
656
|
_inherits(ReplaceAroundStep, _Step6);
|
|
@@ -1507,6 +1508,18 @@ function _replaceRangeWith(tr, from, to, node) {
|
|
|
1507
1508
|
function _deleteRange(tr, from, to) {
|
|
1508
1509
|
var $from = tr.doc.resolve(from),
|
|
1509
1510
|
$to = tr.doc.resolve(to);
|
|
1511
|
+
if ($from.parent.isTextblock && $to.parent.isTextblock && $from.start() != $to.start() && $from.parentOffset == 0 && $to.parentOffset == 0) {
|
|
1512
|
+
var shared = $from.sharedDepth(to),
|
|
1513
|
+
isolated = false;
|
|
1514
|
+
for (var d = $from.depth; d > shared; d--) if ($from.node(d).type.spec.isolating) isolated = true;
|
|
1515
|
+
for (var _d4 = $to.depth; _d4 > shared; _d4--) if ($to.node(_d4).type.spec.isolating) isolated = true;
|
|
1516
|
+
if (!isolated) {
|
|
1517
|
+
for (var _d5 = $from.depth; _d5 > 0 && from == $from.start(_d5); _d5--) from = $from.before(_d5);
|
|
1518
|
+
for (var _d6 = $to.depth; _d6 > 0 && to == $to.start(_d6); _d6--) to = $to.before(_d6);
|
|
1519
|
+
$from = tr.doc.resolve(from);
|
|
1520
|
+
$to = tr.doc.resolve(to);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1510
1523
|
var covered = coveredDepths($from, $to);
|
|
1511
1524
|
for (var i = 0; i < covered.length; i++) {
|
|
1512
1525
|
var depth = covered[i],
|
|
@@ -1514,8 +1527,8 @@ function _deleteRange(tr, from, to) {
|
|
|
1514
1527
|
if (last && depth == 0 || $from.node(depth).type.contentMatch.validEnd) return tr["delete"]($from.start(depth), $to.end(depth));
|
|
1515
1528
|
if (depth > 0 && (last || $from.node(depth - 1).canReplace($from.index(depth - 1), $to.indexAfter(depth - 1)))) return tr["delete"]($from.before(depth), $to.after(depth));
|
|
1516
1529
|
}
|
|
1517
|
-
for (var
|
|
1518
|
-
if (from - $from.start(
|
|
1530
|
+
for (var _d7 = 1; _d7 <= $from.depth && _d7 <= $to.depth; _d7++) {
|
|
1531
|
+
if (from - $from.start(_d7) == $from.depth - _d7 && to > $from.end(_d7) && $to.end(_d7) - to != $to.depth - _d7 && $from.start(_d7 - 1) == $to.start(_d7 - 1) && $from.node(_d7 - 1).canReplace($from.index(_d7 - 1), $to.index(_d7 - 1))) return tr["delete"]($from.before(_d7), to);
|
|
1519
1532
|
}
|
|
1520
1533
|
tr["delete"](from, to);
|
|
1521
1534
|
}
|
|
@@ -1691,6 +1704,27 @@ var Transform = function () {
|
|
|
1691
1704
|
get: function get() {
|
|
1692
1705
|
return this.steps.length > 0;
|
|
1693
1706
|
}
|
|
1707
|
+
}, {
|
|
1708
|
+
key: "changedRange",
|
|
1709
|
+
value: function changedRange() {
|
|
1710
|
+
var from = 1e9,
|
|
1711
|
+
to = -1e9;
|
|
1712
|
+
for (var i = 0; i < this.mapping.maps.length; i++) {
|
|
1713
|
+
var map = this.mapping.maps[i];
|
|
1714
|
+
if (i) {
|
|
1715
|
+
from = map.map(from, 1);
|
|
1716
|
+
to = map.map(to, -1);
|
|
1717
|
+
}
|
|
1718
|
+
map.forEach(function (_f, _t, fromB, toB) {
|
|
1719
|
+
from = Math.min(from, fromB);
|
|
1720
|
+
to = Math.max(to, toB);
|
|
1721
|
+
});
|
|
1722
|
+
}
|
|
1723
|
+
return from == 1e9 ? null : {
|
|
1724
|
+
from: from,
|
|
1725
|
+
to: to
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1694
1728
|
}, {
|
|
1695
1729
|
key: "addStep",
|
|
1696
1730
|
value: function addStep(step, doc) {
|
package/dist/index.d.cts
CHANGED
|
@@ -326,6 +326,16 @@ declare class Transform {
|
|
|
326
326
|
*/
|
|
327
327
|
get docChanged(): boolean;
|
|
328
328
|
/**
|
|
329
|
+
Return a single range, in post-transform document positions,
|
|
330
|
+
that covers all content changed by this transform. Returns null
|
|
331
|
+
if no replacements are made. Note that this will ignore changes
|
|
332
|
+
that add/remove marks without replacing the underlying content.
|
|
333
|
+
*/
|
|
334
|
+
changedRange(): {
|
|
335
|
+
from: number;
|
|
336
|
+
to: number;
|
|
337
|
+
} | null;
|
|
338
|
+
/**
|
|
329
339
|
Replace the part of the document between `from` and `to` with the
|
|
330
340
|
given `slice`.
|
|
331
341
|
*/
|
|
@@ -694,6 +704,15 @@ declare class ReplaceStep extends Step {
|
|
|
694
704
|
map(mapping: Mappable): ReplaceStep | null;
|
|
695
705
|
merge(other: Step): ReplaceStep | null;
|
|
696
706
|
toJSON(): any;
|
|
707
|
+
/**
|
|
708
|
+
By default, for backwards compatibility, an inserting step
|
|
709
|
+
mapped over an insertion at that same position fill move after
|
|
710
|
+
the inserted content. In a collaborative editing situation, that
|
|
711
|
+
can make redone insertions appear in unexpected places. You can
|
|
712
|
+
set this to -1 to make such mapping keep the step before the
|
|
713
|
+
insertion instead.
|
|
714
|
+
*/
|
|
715
|
+
static MAP_BIAS: -1 | 1;
|
|
697
716
|
}
|
|
698
717
|
/**
|
|
699
718
|
Replace a part of the document with a slice of content, but
|
package/dist/index.d.ts
CHANGED
|
@@ -326,6 +326,16 @@ declare class Transform {
|
|
|
326
326
|
*/
|
|
327
327
|
get docChanged(): boolean;
|
|
328
328
|
/**
|
|
329
|
+
Return a single range, in post-transform document positions,
|
|
330
|
+
that covers all content changed by this transform. Returns null
|
|
331
|
+
if no replacements are made. Note that this will ignore changes
|
|
332
|
+
that add/remove marks without replacing the underlying content.
|
|
333
|
+
*/
|
|
334
|
+
changedRange(): {
|
|
335
|
+
from: number;
|
|
336
|
+
to: number;
|
|
337
|
+
} | null;
|
|
338
|
+
/**
|
|
329
339
|
Replace the part of the document between `from` and `to` with the
|
|
330
340
|
given `slice`.
|
|
331
341
|
*/
|
|
@@ -694,6 +704,15 @@ declare class ReplaceStep extends Step {
|
|
|
694
704
|
map(mapping: Mappable): ReplaceStep | null;
|
|
695
705
|
merge(other: Step): ReplaceStep | null;
|
|
696
706
|
toJSON(): any;
|
|
707
|
+
/**
|
|
708
|
+
By default, for backwards compatibility, an inserting step
|
|
709
|
+
mapped over an insertion at that same position fill move after
|
|
710
|
+
the inserted content. In a collaborative editing situation, that
|
|
711
|
+
can make redone insertions appear in unexpected places. You can
|
|
712
|
+
set this to -1 to make such mapping keep the step before the
|
|
713
|
+
insertion instead.
|
|
714
|
+
*/
|
|
715
|
+
static MAP_BIAS: -1 | 1;
|
|
697
716
|
}
|
|
698
717
|
/**
|
|
699
718
|
Replace a part of the document with a slice of content, but
|
package/dist/index.js
CHANGED
|
@@ -711,7 +711,8 @@ class ReplaceStep extends Step {
|
|
|
711
711
|
return new ReplaceStep(this.from, this.from + this.slice.size, doc.slice(this.from, this.to));
|
|
712
712
|
}
|
|
713
713
|
map(mapping) {
|
|
714
|
-
let
|
|
714
|
+
let to = mapping.mapResult(this.to, -1);
|
|
715
|
+
let from = this.from == this.to && ReplaceStep.MAP_BIAS < 0 ? to : mapping.mapResult(this.from, 1);
|
|
715
716
|
if (from.deletedAcross && to.deletedAcross)
|
|
716
717
|
return null;
|
|
717
718
|
return new ReplaceStep(from.pos, Math.max(from.pos, to.pos), this.slice, this.structure);
|
|
@@ -750,6 +751,15 @@ class ReplaceStep extends Step {
|
|
|
750
751
|
return new ReplaceStep(json.from, json.to, Slice.fromJSON(schema, json.slice), !!json.structure);
|
|
751
752
|
}
|
|
752
753
|
}
|
|
754
|
+
/**
|
|
755
|
+
By default, for backwards compatibility, an inserting step
|
|
756
|
+
mapped over an insertion at that same position fill move after
|
|
757
|
+
the inserted content. In a collaborative editing situation, that
|
|
758
|
+
can make redone insertions appear in unexpected places. You can
|
|
759
|
+
set this to -1 to make such mapping keep the step before the
|
|
760
|
+
insertion instead.
|
|
761
|
+
*/
|
|
762
|
+
ReplaceStep.MAP_BIAS = 1;
|
|
753
763
|
Step.jsonID("replace", ReplaceStep);
|
|
754
764
|
/**
|
|
755
765
|
Replace a part of the document with a slice of content, but
|
|
@@ -1736,6 +1746,26 @@ function replaceRangeWith(tr, from, to, node) {
|
|
|
1736
1746
|
}
|
|
1737
1747
|
function deleteRange(tr, from, to) {
|
|
1738
1748
|
let $from = tr.doc.resolve(from), $to = tr.doc.resolve(to);
|
|
1749
|
+
// When the deleted range spans from the start of one textblock to
|
|
1750
|
+
// the start of another one, move out of the start of both blocks.
|
|
1751
|
+
if ($from.parent.isTextblock && $to.parent.isTextblock && $from.start() != $to.start() &&
|
|
1752
|
+
$from.parentOffset == 0 && $to.parentOffset == 0) {
|
|
1753
|
+
let shared = $from.sharedDepth(to), isolated = false;
|
|
1754
|
+
for (let d = $from.depth; d > shared; d--)
|
|
1755
|
+
if ($from.node(d).type.spec.isolating)
|
|
1756
|
+
isolated = true;
|
|
1757
|
+
for (let d = $to.depth; d > shared; d--)
|
|
1758
|
+
if ($to.node(d).type.spec.isolating)
|
|
1759
|
+
isolated = true;
|
|
1760
|
+
if (!isolated) {
|
|
1761
|
+
for (let d = $from.depth; d > 0 && from == $from.start(d); d--)
|
|
1762
|
+
from = $from.before(d);
|
|
1763
|
+
for (let d = $to.depth; d > 0 && to == $to.start(d); d--)
|
|
1764
|
+
to = $to.before(d);
|
|
1765
|
+
$from = tr.doc.resolve(from);
|
|
1766
|
+
$to = tr.doc.resolve(to);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1739
1769
|
let covered = coveredDepths($from, $to);
|
|
1740
1770
|
for (let i = 0; i < covered.length; i++) {
|
|
1741
1771
|
let depth = covered[i], last = i == covered.length - 1;
|
|
@@ -1946,6 +1976,27 @@ class Transform {
|
|
|
1946
1976
|
return this.steps.length > 0;
|
|
1947
1977
|
}
|
|
1948
1978
|
/**
|
|
1979
|
+
Return a single range, in post-transform document positions,
|
|
1980
|
+
that covers all content changed by this transform. Returns null
|
|
1981
|
+
if no replacements are made. Note that this will ignore changes
|
|
1982
|
+
that add/remove marks without replacing the underlying content.
|
|
1983
|
+
*/
|
|
1984
|
+
changedRange() {
|
|
1985
|
+
let from = 1e9, to = -1e9;
|
|
1986
|
+
for (let i = 0; i < this.mapping.maps.length; i++) {
|
|
1987
|
+
let map = this.mapping.maps[i];
|
|
1988
|
+
if (i) {
|
|
1989
|
+
from = map.map(from, 1);
|
|
1990
|
+
to = map.map(to, -1);
|
|
1991
|
+
}
|
|
1992
|
+
map.forEach((_f, _t, fromB, toB) => {
|
|
1993
|
+
from = Math.min(from, fromB);
|
|
1994
|
+
to = Math.max(to, toB);
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1997
|
+
return from == 1e9 ? null : { from, to };
|
|
1998
|
+
}
|
|
1999
|
+
/**
|
|
1949
2000
|
@internal
|
|
1950
2001
|
*/
|
|
1951
2002
|
addStep(step, doc) {
|
package/package.json
CHANGED
package/src/replace.ts
CHANGED
|
@@ -425,6 +425,22 @@ export function replaceRangeWith(tr: Transform, from: number, to: number, node:
|
|
|
425
425
|
|
|
426
426
|
export function deleteRange(tr: Transform, from: number, to: number) {
|
|
427
427
|
let $from = tr.doc.resolve(from), $to = tr.doc.resolve(to)
|
|
428
|
+
|
|
429
|
+
// When the deleted range spans from the start of one textblock to
|
|
430
|
+
// the start of another one, move out of the start of both blocks.
|
|
431
|
+
if ($from.parent.isTextblock && $to.parent.isTextblock && $from.start() != $to.start() &&
|
|
432
|
+
$from.parentOffset == 0 && $to.parentOffset == 0) {
|
|
433
|
+
let shared = $from.sharedDepth(to), isolated = false
|
|
434
|
+
for (let d = $from.depth; d > shared; d--) if ($from.node(d).type.spec.isolating) isolated = true
|
|
435
|
+
for (let d = $to.depth; d > shared; d--) if ($to.node(d).type.spec.isolating) isolated = true
|
|
436
|
+
if (!isolated) {
|
|
437
|
+
for (let d = $from.depth; d > 0 && from == $from.start(d); d--) from = $from.before(d)
|
|
438
|
+
for (let d = $to.depth; d > 0 && to == $to.start(d); d--) to = $to.before(d)
|
|
439
|
+
$from = tr.doc.resolve(from)
|
|
440
|
+
$to = tr.doc.resolve(to)
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
428
444
|
let covered = coveredDepths($from, $to)
|
|
429
445
|
for (let i = 0; i < covered.length; i++) {
|
|
430
446
|
let depth = covered[i], last = i == covered.length - 1
|
package/src/replace_step.ts
CHANGED
|
@@ -40,7 +40,8 @@ export class ReplaceStep extends Step {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
map(mapping: Mappable) {
|
|
43
|
-
let
|
|
43
|
+
let to = mapping.mapResult(this.to, -1)
|
|
44
|
+
let from = this.from == this.to && ReplaceStep.MAP_BIAS < 0 ? to : mapping.mapResult(this.from, 1)
|
|
44
45
|
if (from.deletedAcross && to.deletedAcross) return null
|
|
45
46
|
return new ReplaceStep(from.pos, Math.max(from.pos, to.pos), this.slice, this.structure)
|
|
46
47
|
}
|
|
@@ -74,6 +75,14 @@ export class ReplaceStep extends Step {
|
|
|
74
75
|
throw new RangeError("Invalid input for ReplaceStep.fromJSON")
|
|
75
76
|
return new ReplaceStep(json.from, json.to, Slice.fromJSON(schema, json.slice), !!json.structure)
|
|
76
77
|
}
|
|
78
|
+
|
|
79
|
+
/// By default, for backwards compatibility, an inserting step
|
|
80
|
+
/// mapped over an insertion at that same position fill move after
|
|
81
|
+
/// the inserted content. In a collaborative editing situation, that
|
|
82
|
+
/// can make redone insertions appear in unexpected places. You can
|
|
83
|
+
/// set this to -1 to make such mapping keep the step before the
|
|
84
|
+
/// insertion instead.
|
|
85
|
+
static MAP_BIAS: -1 | 1 = 1
|
|
77
86
|
}
|
|
78
87
|
|
|
79
88
|
Step.jsonID("replace", ReplaceStep)
|
package/src/transform.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {Node, NodeType, Mark, MarkType, ContentMatch, Slice, Fragment, NodeRange, Attrs} from "prosemirror-model"
|
|
2
|
-
|
|
3
2
|
import {Mapping} from "./map"
|
|
4
3
|
import {Step} from "./step"
|
|
5
4
|
import {addMark, removeMark, clearIncompatible} from "./mark"
|
|
@@ -66,6 +65,26 @@ export class Transform {
|
|
|
66
65
|
return this.steps.length > 0
|
|
67
66
|
}
|
|
68
67
|
|
|
68
|
+
/// Return a single range, in post-transform document positions,
|
|
69
|
+
/// that covers all content changed by this transform. Returns null
|
|
70
|
+
/// if no replacements are made. Note that this will ignore changes
|
|
71
|
+
/// that add/remove marks without replacing the underlying content.
|
|
72
|
+
changedRange() {
|
|
73
|
+
let from = 1e9, to = -1e9
|
|
74
|
+
for (let i = 0; i < this.mapping.maps.length; i++) {
|
|
75
|
+
let map = this.mapping.maps[i]
|
|
76
|
+
if (i) {
|
|
77
|
+
from = map.map(from, 1)
|
|
78
|
+
to = map.map(to, -1)
|
|
79
|
+
}
|
|
80
|
+
map.forEach((_f, _t, fromB, toB) => {
|
|
81
|
+
from = Math.min(from, fromB)
|
|
82
|
+
to = Math.max(to, toB)
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
return from == 1e9 ? null : {from, to}
|
|
86
|
+
}
|
|
87
|
+
|
|
69
88
|
/// @internal
|
|
70
89
|
addStep(step: Step, doc: Node) {
|
|
71
90
|
this.docs.push(this.doc)
|
|
@@ -206,7 +225,7 @@ export class Transform {
|
|
|
206
225
|
if (mark instanceof Mark) {
|
|
207
226
|
if (mark.isInSet(node.marks)) this.step(new RemoveNodeMarkStep(pos, mark))
|
|
208
227
|
} else {
|
|
209
|
-
let set = node.marks, found, steps: Step[] = []
|
|
228
|
+
let set = node.marks, found: Mark | undefined, steps: Step[] = []
|
|
210
229
|
while (found = mark.isInSet(set)) {
|
|
211
230
|
steps.push(new RemoveNodeMarkStep(pos, found))
|
|
212
231
|
set = found.removeFromSet(set)
|