@stll/folio-core 0.54.0 → 0.54.2
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/dist/ai-edits/apply.d.ts +1 -1
- package/dist/ai-edits/apply.js +43 -31
- package/dist/ai-edits/headless.d.ts +1 -2
- package/dist/ai-edits/headless.js +12 -27
- package/dist/ai-edits/read.d.ts +1 -0
- package/dist/ai-edits/read.js +7 -2
- package/dist/ai-edits/table-mutation-plan.d.ts +2 -1
- package/dist/ai-edits/table-mutation-plan.js +39 -1
- package/dist/ai-suggestions/apply.d.ts +1 -1
- package/dist/ai-suggestions/apply.js +3 -2
- package/dist/compare/compare.js +2 -1
- package/dist/docx/bookmarkIds.d.ts +11 -0
- package/dist/docx/bookmarkIds.js +33 -0
- package/dist/docx/documentParser.d.ts +14 -1
- package/dist/docx/documentParser.js +17 -4
- package/dist/docx/drawingIdNormalization.js +23 -8
- package/dist/docx/ensureParaIds.d.ts +5 -0
- package/dist/docx/ensureParaIds.js +10 -2
- package/dist/docx/listNumberingInstances.js +11 -9
- package/dist/docx/noteIds.d.ts +4 -0
- package/dist/docx/noteIds.js +13 -0
- package/dist/docx/numberingIds.d.ts +14 -0
- package/dist/docx/numberingIds.js +16 -0
- package/dist/docx/numericIdAllocator.d.ts +12 -0
- package/dist/docx/numericIdAllocator.js +33 -0
- package/dist/docx/numericIdNormalization.d.ts +16 -0
- package/dist/docx/numericIdNormalization.js +174 -0
- package/dist/docx/paragraphPropertySource.js +1 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +2 -1
- package/dist/docx/parser.js +17 -5
- package/dist/docx/rasterMime.d.ts +64 -0
- package/dist/docx/rasterMime.js +116 -0
- package/dist/docx/replyToComment.js +13 -6
- package/dist/docx/rezip.js +14 -7
- package/dist/docx/selectiveSave.js +10 -0
- package/dist/docx/serializer/partNamespaces.js +4 -2
- package/dist/docx/serializer/runSerializer.js +5 -4
- package/dist/docx/serializer/trackedChangeAttributes.js +7 -1
- package/dist/docx/server/build.js +14 -3
- package/dist/docx/server/createBilingualDocument.js +10 -9
- package/dist/docx/streamingXmlParser.d.ts +10 -1
- package/dist/docx/streamingXmlParser.js +88 -19
- package/dist/docx/unzip.d.ts +3 -1
- package/dist/docx/unzip.js +36 -16
- package/dist/docx/verticalMergeProjection.d.ts +17 -0
- package/dist/docx/verticalMergeProjection.js +120 -0
- package/dist/layout-bridge/convert/fixedTableColumnWidths.d.ts +6 -0
- package/dist/layout-bridge/convert/fixedTableColumnWidths.js +23 -0
- package/dist/layout-bridge/convert/tableConversion.js +4 -1
- package/dist/layout-painter/index.js +2 -5
- package/dist/paged-layout/editorScrollRoot.d.ts +15 -0
- package/dist/paged-layout/editorScrollRoot.js +72 -0
- package/dist/paged-layout/scrollToPmPosition.js +8 -22
- package/dist/prosemirror/commands/clearParagraphIndent.d.ts +6 -0
- package/dist/prosemirror/commands/clearParagraphIndent.js +25 -0
- package/dist/prosemirror/commands/comments.js +6 -1
- package/dist/prosemirror/commands/propertyChangeScope.js +4 -1
- package/dist/prosemirror/commands/resolveAllTableChanges.js +18 -24
- package/dist/prosemirror/commands/resolveParagraphProperties.js +13 -2
- package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +8 -9
- package/dist/prosemirror/commands/tableCellMergeResolution.js +28 -25
- package/dist/prosemirror/commands/tableMergeFoldDecisions.d.ts +61 -0
- package/dist/prosemirror/commands/tableMergeFoldDecisions.js +79 -0
- package/dist/prosemirror/commentIdAllocator.d.ts +15 -22
- package/dist/prosemirror/commentIdAllocator.js +50 -25
- package/dist/prosemirror/conversion/toProseDoc.d.ts +1 -6
- package/dist/prosemirror/conversion/toProseDoc.js +2 -117
- package/dist/prosemirror/extensions/core/HistoryExtension.js +97 -5
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +3 -1
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +22 -28
- package/dist/prosemirror/extensions/nodes/ShapeExtension.js +8 -6
- package/dist/prosemirror/listNumbering.js +14 -14
- package/dist/prosemirror/listRendering.d.ts +4 -2
- package/dist/prosemirror/listRendering.js +3 -4
- package/dist/prosemirror/paragraphPropertyCarry.js +2 -5
- package/dist/prosemirror/plugins/revisionIds.d.ts +23 -19
- package/dist/prosemirror/plugins/revisionIds.js +110 -29
- package/dist/prosemirror/plugins/suggestionMode.d.ts +2 -2
- package/dist/prosemirror/plugins/suggestionMode.js +197 -45
- package/dist/prosemirror/plugins/templateDirectives.d.ts +6 -0
- package/dist/prosemirror/plugins/templateDirectives.js +21 -7
- package/dist/prosemirror/plugins/templatePreviewValues.js +14 -21
- package/dist/prosemirror/storyListNumbering.js +11 -9
- package/dist/prosemirror/textInput.d.ts +6 -1
- package/dist/prosemirror/textInput.js +9 -2
- package/dist/prosemirror/utils/visualLineNavigation.js +6 -8
- package/dist/utils/mergeDocumentContent.d.ts +2 -2
- package/dist/utils/mergeDocumentContent.js +11 -8
- package/package.json +3 -3
|
@@ -5,17 +5,16 @@ import { Transaction } from "prosemirror-state";
|
|
|
5
5
|
//#region src/prosemirror/commands/tableCellMergeResolution.d.ts
|
|
6
6
|
declare const tableCellContinuationPayload: (node: Node) => DecodedTableCellParagraphSourcePayload | null;
|
|
7
7
|
declare const hasMatchingCollapsedTableCellMerge: (node: Node, revisionSet: Set<number> | null) => boolean;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
declare const
|
|
15
|
-
declare const resolveVisibleTableCellMerge: (tr: Transaction, cellPos: number, mode: "accept" | "reject") => boolean;
|
|
8
|
+
type ResolveVisibleTableCellMergeOptions = {
|
|
9
|
+
tr: Transaction;
|
|
10
|
+
cellPos: number;
|
|
11
|
+
mode: "accept" | "reject";
|
|
12
|
+
revisionSet: ReadonlySet<number> | null;
|
|
13
|
+
};
|
|
14
|
+
declare const resolveVisibleTableCellMerge: ({ tr, cellPos, mode, revisionSet }: ResolveVisibleTableCellMergeOptions) => boolean;
|
|
16
15
|
declare const tableCellContinuationCells: (cell: Node, rowspan: number) => document_d_exports.TableCell[];
|
|
17
16
|
declare const tableCellContinuationFromNode: (cell: Node) => document_d_exports.TableCell;
|
|
18
17
|
declare const resolveCollapsedTableCellMerge: (tr: Transaction, cellPos: number, mode: "accept" | "reject", revisionSet: Set<number> | null) => boolean;
|
|
19
18
|
declare const createRestoredTableCell: (origin: Node, source: document_d_exports.TableCell) => Node | null;
|
|
20
19
|
//#endregion
|
|
21
|
-
export {
|
|
20
|
+
export { createRestoredTableCell, hasMatchingCollapsedTableCellMerge, resolveCollapsedTableCellMerge, resolveVisibleTableCellMerge, tableCellContinuationCells, tableCellContinuationFromNode, tableCellContinuationPayload };
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { decodeTableCellParagraphSourcePayload, restoreTableCellsWithParagraphPropertySources, transportTableCellsWithParagraphPropertySources } from "../../docx/paragraphPropertySource.js";
|
|
2
2
|
import { isTableCellMergeRevisionContinuation } from "../../docx/tableParser.js";
|
|
3
3
|
import { standaloneTableCellFromProseMirror } from "../conversion/fromProseDoc.js";
|
|
4
|
-
import { standaloneTableCellToProseMirror
|
|
4
|
+
import { standaloneTableCellToProseMirror } from "../conversion/toProseDoc.js";
|
|
5
5
|
import { getTableCellMergeChange } from "../tableCellMergeRevision.js";
|
|
6
6
|
import { removeRowsWithoutCellsInRange } from "../tableGridMutation.js";
|
|
7
|
+
import { resolvedTableMergeOriginAttrs, resolvedVisibleTableCellMergeAttrs, tableMergeFoldDecisions } from "./tableMergeFoldDecisions.js";
|
|
7
8
|
import { Result, panic } from "better-result";
|
|
8
9
|
import { TableMap } from "prosemirror-tables";
|
|
9
10
|
//#region src/prosemirror/commands/tableCellMergeResolution.ts
|
|
@@ -37,33 +38,35 @@ const tableCellContext = (doc, cellPos) => {
|
|
|
37
38
|
}
|
|
38
39
|
return null;
|
|
39
40
|
};
|
|
40
|
-
|
|
41
|
-
* Whether accepting a pending vertical merge folds the cell into the one above.
|
|
42
|
-
* The saved `w:vMerge` continuation is read back as its own cell when it carries
|
|
43
|
-
* content, so accepting such a merge keeps the cell standing; folding it would
|
|
44
|
-
* hide text that reappears when the document is reopened.
|
|
45
|
-
*/
|
|
46
|
-
const acceptedMergeFoldsIntoCellAbove = (cell) => !tableCellHasMeaningfulContent(standaloneTableCellFromProseMirror(cell));
|
|
47
|
-
const resolveVisibleTableCellMerge = (tr, cellPos, mode) => {
|
|
41
|
+
const resolveVisibleTableCellMerge = ({ tr, cellPos, mode, revisionSet }) => {
|
|
48
42
|
const cell = tr.doc.nodeAt(cellPos);
|
|
49
43
|
const marker = cell?.attrs["cellMarker"];
|
|
50
44
|
if (!cell || !isTableCellMergeRevisionAttr(marker)) return false;
|
|
51
|
-
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
nextOriginalFormatting = { ...originalFormatting };
|
|
60
|
-
delete nextOriginalFormatting.vMerge;
|
|
61
|
-
}
|
|
62
|
-
tr.setNodeMarkup(cellPos, void 0, {
|
|
63
|
-
...cell.attrs,
|
|
64
|
-
cellMarker: null,
|
|
65
|
-
_originalFormatting: nextOriginalFormatting
|
|
45
|
+
const context = tableCellContext(tr.doc, cellPos);
|
|
46
|
+
if (!context) return false;
|
|
47
|
+
const rectangle = TableMap.get(context.table).findCell(context.relativeCellPos);
|
|
48
|
+
if (mode === "reject" && isTableCellMergeRevisionContinuation(marker.verticalMergeOriginal) && rectangle.top === 0) return false;
|
|
49
|
+
const decisions = tableMergeFoldDecisions({
|
|
50
|
+
table: context.table,
|
|
51
|
+
mode,
|
|
52
|
+
revisionSet
|
|
66
53
|
});
|
|
54
|
+
if (decisions.invalid.has(context.relativeCellPos)) return false;
|
|
55
|
+
const folds = decisions.folds.get(context.relativeCellPos);
|
|
56
|
+
if (folds === void 0) return panic("Merge resolution lost its cell");
|
|
57
|
+
for (const origin of decisions.origins) {
|
|
58
|
+
if (origin === context.relativeCellPos) continue;
|
|
59
|
+
const originPos = context.tableStart + origin;
|
|
60
|
+
const originCell = tr.doc.nodeAt(originPos);
|
|
61
|
+
if (!originCell) return panic("Merge resolution lost its origin");
|
|
62
|
+
tr.setNodeMarkup(originPos, void 0, resolvedTableMergeOriginAttrs(originCell));
|
|
63
|
+
}
|
|
64
|
+
if (folds) return mergeTableCellWithCellAbove(tr, cellPos);
|
|
65
|
+
tr.setNodeMarkup(cellPos, void 0, resolvedVisibleTableCellMergeAttrs({
|
|
66
|
+
cell,
|
|
67
|
+
mode,
|
|
68
|
+
origin: decisions.origins.has(context.relativeCellPos)
|
|
69
|
+
}));
|
|
67
70
|
return true;
|
|
68
71
|
};
|
|
69
72
|
const mergeTableCellWithCellAbove = (tr, cellPos) => {
|
|
@@ -201,4 +204,4 @@ const createRestoredTableCell = (origin, source) => {
|
|
|
201
204
|
return Result.try(() => origin.type.schema.nodeFromJSON(restored.toJSON())).unwrapOr(null);
|
|
202
205
|
};
|
|
203
206
|
//#endregion
|
|
204
|
-
export {
|
|
207
|
+
export { createRestoredTableCell, hasMatchingCollapsedTableCellMerge, resolveCollapsedTableCellMerge, resolveVisibleTableCellMerge, tableCellContinuationCells, tableCellContinuationFromNode, tableCellContinuationPayload };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Node } from "prosemirror-model";
|
|
2
|
+
//#region src/prosemirror/commands/tableMergeFoldDecisions.d.ts
|
|
3
|
+
type TableMergeFoldDecisionsOptions = {
|
|
4
|
+
table: Node;
|
|
5
|
+
mode: "accept" | "reject";
|
|
6
|
+
revisionSet: ReadonlySet<number> | null;
|
|
7
|
+
};
|
|
8
|
+
/** Resolve raw merge states without converting unrelated table revisions. */
|
|
9
|
+
declare const tableMergeFoldDecisions: ({ table, mode, revisionSet }: TableMergeFoldDecisionsOptions) => {
|
|
10
|
+
folds: Map<number, boolean>;
|
|
11
|
+
origins: Set<number>;
|
|
12
|
+
invalid: Set<number>;
|
|
13
|
+
};
|
|
14
|
+
/** Persist an implicit merge origin even when reader rules retain its continuation. */
|
|
15
|
+
declare const resolvedTableMergeOriginAttrs: (cell: Node) => {
|
|
16
|
+
_originalFormatting: {
|
|
17
|
+
vMerge: string;
|
|
18
|
+
width?: import("@stll/docx-core/model").TableMeasurement;
|
|
19
|
+
borders?: import("@stll/docx-core/model").TableCellBorders;
|
|
20
|
+
margins?: import("@stll/docx-core/model").CellMargins;
|
|
21
|
+
shading?: import("@stll/docx-core/model").ShadingProperties;
|
|
22
|
+
verticalAlign?: "top" | "center" | "bottom";
|
|
23
|
+
textDirection?: import("@stll/docx-core/model").TextDirection;
|
|
24
|
+
gridSpan?: number;
|
|
25
|
+
fitText?: boolean;
|
|
26
|
+
noWrap?: boolean;
|
|
27
|
+
hideMark?: boolean;
|
|
28
|
+
conditionalFormat?: import("@stll/docx-core/model").ConditionalFormatStyle;
|
|
29
|
+
preserved?: import("@stll/docx-core/model").PreservedMarkup;
|
|
30
|
+
sourceXml?: string;
|
|
31
|
+
};
|
|
32
|
+
_preserveVMergeRestart: boolean;
|
|
33
|
+
};
|
|
34
|
+
type ResolvedVisibleTableCellMergeOptions = {
|
|
35
|
+
cell: Node;
|
|
36
|
+
mode: "accept" | "reject";
|
|
37
|
+
origin: boolean;
|
|
38
|
+
};
|
|
39
|
+
/** A retained continuation must save the merge state the reviewer resolved. */
|
|
40
|
+
declare const resolvedVisibleTableCellMergeAttrs: ({ cell, mode, origin }: ResolvedVisibleTableCellMergeOptions) => {
|
|
41
|
+
cellMarker: null;
|
|
42
|
+
_originalFormatting: {
|
|
43
|
+
width?: import("@stll/docx-core/model").TableMeasurement;
|
|
44
|
+
borders?: import("@stll/docx-core/model").TableCellBorders;
|
|
45
|
+
margins?: import("@stll/docx-core/model").CellMargins;
|
|
46
|
+
shading?: import("@stll/docx-core/model").ShadingProperties;
|
|
47
|
+
verticalAlign?: "top" | "center" | "bottom";
|
|
48
|
+
textDirection?: import("@stll/docx-core/model").TextDirection;
|
|
49
|
+
gridSpan?: number;
|
|
50
|
+
vMerge?: "restart" | "continue";
|
|
51
|
+
fitText?: boolean;
|
|
52
|
+
noWrap?: boolean;
|
|
53
|
+
hideMark?: boolean;
|
|
54
|
+
conditionalFormat?: import("@stll/docx-core/model").ConditionalFormatStyle;
|
|
55
|
+
preserved?: import("@stll/docx-core/model").PreservedMarkup;
|
|
56
|
+
sourceXml?: string;
|
|
57
|
+
};
|
|
58
|
+
_preserveVMergeRestart: boolean;
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
61
|
+
export { resolvedTableMergeOriginAttrs, resolvedVisibleTableCellMergeAttrs, tableMergeFoldDecisions };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { isCellMergeContinuation, isCellMergeStart, isTableCellMergeRevisionContinuation } from "../../docx/tableParser.js";
|
|
2
|
+
import { tableCellHasMeaningfulContent } from "../../docx/verticalMergeProjection.js";
|
|
3
|
+
import { expectTableCellAttrs } from "../attrs/index.js";
|
|
4
|
+
import { standaloneTableCellFromProseMirror } from "../conversion/fromProseDoc.js";
|
|
5
|
+
import { panic } from "better-result";
|
|
6
|
+
import { TableMap } from "prosemirror-tables";
|
|
7
|
+
//#region src/prosemirror/commands/tableMergeFoldDecisions.ts
|
|
8
|
+
/** Resolve raw merge states without converting unrelated table revisions. */
|
|
9
|
+
const tableMergeFoldDecisions = ({ table, mode, revisionSet }) => {
|
|
10
|
+
const decisions = /* @__PURE__ */ new Map();
|
|
11
|
+
const origins = /* @__PURE__ */ new Set();
|
|
12
|
+
const invalid = /* @__PURE__ */ new Set();
|
|
13
|
+
const map = TableMap.get(table);
|
|
14
|
+
const visited = /* @__PURE__ */ new Set();
|
|
15
|
+
for (const [index, position] of map.map.entries()) {
|
|
16
|
+
if (visited.has(position)) continue;
|
|
17
|
+
visited.add(position);
|
|
18
|
+
const cell = table.nodeAt(position);
|
|
19
|
+
if (cell?.attrs["cellMarker"]?.kind !== "merge") continue;
|
|
20
|
+
const marker = expectTableCellAttrs(cell).cellMarker;
|
|
21
|
+
if (marker?.kind !== "merge" || revisionSet !== null && !revisionSet.has(marker.info.revisionId)) continue;
|
|
22
|
+
const continues = isTableCellMergeRevisionContinuation(mode === "accept" ? marker.verticalMerge : marker.verticalMergeOriginal);
|
|
23
|
+
decisions.set(position, continues && (mode === "reject" || !tableCellHasMeaningfulContent(standaloneTableCellFromProseMirror(cell))));
|
|
24
|
+
if (!continues) continue;
|
|
25
|
+
if (Math.floor(index / map.width) === 0) {
|
|
26
|
+
invalid.add(position);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const abovePosition = map.map[index - map.width];
|
|
30
|
+
if (abovePosition === void 0) return panic("Merge resolution lost its origin");
|
|
31
|
+
const above = table.nodeAt(abovePosition);
|
|
32
|
+
if (!above) return panic("Merge resolution lost its origin");
|
|
33
|
+
const rectangle = map.findCell(position);
|
|
34
|
+
const aboveRectangle = map.findCell(abovePosition);
|
|
35
|
+
if (rectangle.left !== aboveRectangle.left || rectangle.right !== aboveRectangle.right || rectangle.top !== aboveRectangle.bottom) {
|
|
36
|
+
invalid.add(position);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const aboveMarker = expectTableCellAttrs(above).cellMarker;
|
|
40
|
+
const aboveContinues = aboveMarker?.kind === "merge" && (revisionSet === null || revisionSet.has(aboveMarker.info.revisionId)) && isTableCellMergeRevisionContinuation(mode === "accept" ? aboveMarker.verticalMerge : aboveMarker.verticalMergeOriginal);
|
|
41
|
+
const aboveModel = standaloneTableCellFromProseMirror(above);
|
|
42
|
+
if (mode === "accept" && (aboveContinues && decisions.get(abovePosition) === false || isCellMergeContinuation(aboveModel) && tableCellHasMeaningfulContent(aboveModel))) decisions.set(position, false);
|
|
43
|
+
if (aboveContinues || isCellMergeContinuation(aboveModel) || isCellMergeStart(aboveModel)) continue;
|
|
44
|
+
origins.add(abovePosition);
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
folds: decisions,
|
|
48
|
+
origins,
|
|
49
|
+
invalid
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
/** Persist an implicit merge origin even when reader rules retain its continuation. */
|
|
53
|
+
const resolvedTableMergeOriginAttrs = (cell) => ({
|
|
54
|
+
...cell.attrs,
|
|
55
|
+
_originalFormatting: {
|
|
56
|
+
...expectTableCellAttrs(cell)._originalFormatting,
|
|
57
|
+
vMerge: "restart"
|
|
58
|
+
},
|
|
59
|
+
_preserveVMergeRestart: true
|
|
60
|
+
});
|
|
61
|
+
/** A retained continuation must save the merge state the reviewer resolved. */
|
|
62
|
+
const resolvedVisibleTableCellMergeAttrs = ({ cell, mode, origin }) => {
|
|
63
|
+
const attrs = expectTableCellAttrs(cell);
|
|
64
|
+
const marker = attrs.cellMarker;
|
|
65
|
+
if (marker?.kind !== "merge") return panic("Cell has no merge revision");
|
|
66
|
+
const continuation = mode === "accept" ? marker.verticalMerge : marker.verticalMergeOriginal;
|
|
67
|
+
const formatting = { ...attrs._originalFormatting };
|
|
68
|
+
if (origin) formatting.vMerge = "restart";
|
|
69
|
+
else if (isTableCellMergeRevisionContinuation(continuation)) formatting.vMerge = "continue";
|
|
70
|
+
else delete formatting.vMerge;
|
|
71
|
+
return {
|
|
72
|
+
...cell.attrs,
|
|
73
|
+
cellMarker: null,
|
|
74
|
+
_originalFormatting: formatting,
|
|
75
|
+
_preserveVMergeRestart: origin
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
//#endregion
|
|
79
|
+
export { resolvedTableMergeOriginAttrs, resolvedVisibleTableCellMergeAttrs, tableMergeFoldDecisions };
|
|
@@ -3,30 +3,23 @@ import { EditorView } from "prosemirror-view";
|
|
|
3
3
|
//#region src/prosemirror/commentIdAllocator.d.ts
|
|
4
4
|
/** Sentinel ID for a comment that hasn't been persisted yet (anchored to selection). */
|
|
5
5
|
declare const PENDING_COMMENT_ID = -1;
|
|
6
|
+
declare const CommentIdSpaceExhaustedError_base: import("better-result").TaggedErrorClass<"CommentIdSpaceExhaustedError">;
|
|
7
|
+
declare class CommentIdSpaceExhaustedError extends CommentIdSpaceExhaustedError_base<{
|
|
8
|
+
message: string;
|
|
9
|
+
}> {}
|
|
10
|
+
/** Allocate a valid, unused comment ID in this realm. */
|
|
11
|
+
declare function allocateCommentId(): number;
|
|
12
|
+
/** Reserve a loaded ID and advance past it when the upper bound permits. */
|
|
13
|
+
declare function seedCommentIdAbove(id: number): void;
|
|
6
14
|
type CommentIdAllocator = {
|
|
7
|
-
/** Allocate the next ID
|
|
15
|
+
/** Allocate the next unused ID. */
|
|
8
16
|
next(): number;
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
* loaded comments and tracked-change marks so subsequent allocations don't
|
|
12
|
-
* collide with already-present IDs.
|
|
13
|
-
*/
|
|
14
|
-
seedAbove(maxId: number): void;
|
|
17
|
+
/** Reserve a loaded ID and seed above it when possible. */
|
|
18
|
+
seedAbove(id: number): void;
|
|
15
19
|
};
|
|
16
|
-
/**
|
|
17
|
-
* Create an instance-scoped monotonic comment/revision ID allocator. IDs are
|
|
18
|
-
* never reused (deleting a comment does not free its ID), and the counter is
|
|
19
|
-
* private to this allocator — multiple editors get independent ID spaces.
|
|
20
|
-
*/
|
|
20
|
+
/** Every handle uses the same counter, including separate editor instances. */
|
|
21
21
|
declare function createCommentIdAllocator(): CommentIdAllocator;
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
* — comment objects (including replies, which carry no mark) plus
|
|
25
|
-
* tracked-change `revisionId` marks. Because `seedAbove` only ever raises the
|
|
26
|
-
* counter, this is safe to call on load (React) or before each allocation
|
|
27
|
-
* (Vue): new IDs never collide with or reuse an existing one, and the comment
|
|
28
|
-
* and revision ID spaces stay unified.
|
|
29
|
-
*/
|
|
30
|
-
declare function seedCommentAllocator(allocator: CommentIdAllocator, comments: Comment[] | undefined, view: EditorView | null): void;
|
|
22
|
+
/** Seed from comments, replies, and the document's conservative annotation maximum. */
|
|
23
|
+
declare function seedCommentAllocator(allocator: CommentIdAllocator, comments: Comment[] | undefined, view: Pick<EditorView, "state"> | null): void;
|
|
31
24
|
//#endregion
|
|
32
|
-
export { CommentIdAllocator, PENDING_COMMENT_ID, createCommentIdAllocator, seedCommentAllocator };
|
|
25
|
+
export { CommentIdAllocator, CommentIdSpaceExhaustedError, PENDING_COMMENT_ID, allocateCommentId, createCommentIdAllocator, seedCommentAllocator, seedCommentIdAbove };
|
|
@@ -1,40 +1,65 @@
|
|
|
1
1
|
import { maxAnnotationIdInDoc, seedRevisionIdsAbove } from "./plugins/revisionIds.js";
|
|
2
|
+
import { readRangeAnchorAttrs } from "./rangeAnchorAttrs.js";
|
|
3
|
+
import { TaggedError } from "better-result";
|
|
2
4
|
import { MAX_REVISION_ID } from "@stll/docx-core/model";
|
|
3
5
|
//#region src/prosemirror/commentIdAllocator.ts
|
|
4
|
-
/** Sentinel ID for a comment that hasn't been persisted yet (anchored to selection). */
|
|
5
|
-
const PENDING_COMMENT_ID = -1;
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* private to this allocator — multiple editors get independent ID spaces.
|
|
7
|
+
* Realm-wide comment ID allocation, shared by live editors and headless reviewers.
|
|
8
|
+
* Loaded and issued IDs stay reserved even when a comment is deleted.
|
|
10
9
|
*/
|
|
10
|
+
/** Sentinel ID for a comment that hasn't been persisted yet (anchored to selection). */
|
|
11
|
+
const PENDING_COMMENT_ID = -1;
|
|
12
|
+
var CommentIdSpaceExhaustedError = class extends TaggedError("CommentIdSpaceExhaustedError") {};
|
|
13
|
+
let nextId = 1;
|
|
14
|
+
const reservedIds = /* @__PURE__ */ new Set();
|
|
15
|
+
/** Allocate a valid, unused comment ID in this realm. */
|
|
16
|
+
function allocateCommentId() {
|
|
17
|
+
if (reservedIds.size >= MAX_REVISION_ID) throw new CommentIdSpaceExhaustedError({ message: "The OOXML comment ID space is exhausted." });
|
|
18
|
+
while (reservedIds.has(nextId)) nextId = nextId === MAX_REVISION_ID ? 1 : nextId + 1;
|
|
19
|
+
const id = nextId;
|
|
20
|
+
reservedIds.add(id);
|
|
21
|
+
nextId = id === MAX_REVISION_ID ? 1 : id + 1;
|
|
22
|
+
return id;
|
|
23
|
+
}
|
|
24
|
+
/** Reserve a loaded ID and advance past it when the upper bound permits. */
|
|
25
|
+
function seedCommentIdAbove(id) {
|
|
26
|
+
if (!Number.isInteger(id) || id < 0 || id > MAX_REVISION_ID) return;
|
|
27
|
+
if (id > 0) reservedIds.add(id);
|
|
28
|
+
if (id >= nextId) nextId = id === MAX_REVISION_ID ? 1 : id + 1;
|
|
29
|
+
}
|
|
30
|
+
/** Every handle uses the same counter, including separate editor instances. */
|
|
11
31
|
function createCommentIdAllocator() {
|
|
12
|
-
let nextId = 1;
|
|
13
32
|
return {
|
|
14
|
-
next:
|
|
15
|
-
|
|
16
|
-
return nextId++;
|
|
17
|
-
},
|
|
18
|
-
seedAbove(maxId) {
|
|
19
|
-
if (!Number.isInteger(maxId) || maxId < 0 || maxId >= MAX_REVISION_ID) return;
|
|
20
|
-
if (maxId >= nextId) nextId = maxId + 1;
|
|
21
|
-
}
|
|
33
|
+
next: allocateCommentId,
|
|
34
|
+
seedAbove: seedCommentIdAbove
|
|
22
35
|
};
|
|
23
36
|
}
|
|
24
|
-
/**
|
|
25
|
-
* Seed an allocator above every comment/revision ID currently in the document
|
|
26
|
-
* — comment objects (including replies, which carry no mark) plus
|
|
27
|
-
* tracked-change `revisionId` marks. Because `seedAbove` only ever raises the
|
|
28
|
-
* counter, this is safe to call on load (React) or before each allocation
|
|
29
|
-
* (Vue): new IDs never collide with or reuse an existing one, and the comment
|
|
30
|
-
* and revision ID spaces stay unified.
|
|
31
|
-
*/
|
|
37
|
+
/** Seed from comments, replies, and the document's conservative annotation maximum. */
|
|
32
38
|
function seedCommentAllocator(allocator, comments, view) {
|
|
33
39
|
let max = 0;
|
|
34
|
-
for (const comment of comments ?? [])
|
|
35
|
-
|
|
40
|
+
for (const comment of comments ?? []) {
|
|
41
|
+
allocator.seedAbove(comment.id);
|
|
42
|
+
if (Number.isInteger(comment.id) && comment.id > max && comment.id <= MAX_REVISION_ID) max = comment.id;
|
|
43
|
+
}
|
|
44
|
+
if (view) {
|
|
45
|
+
view.state.doc.descendants((node) => {
|
|
46
|
+
for (const mark of node.marks) {
|
|
47
|
+
const id = mark.attrs["commentId"];
|
|
48
|
+
if (typeof id === "number") allocator.seedAbove(id);
|
|
49
|
+
}
|
|
50
|
+
if (node.type.name === "commentReference") {
|
|
51
|
+
const id = node.attrs["commentId"];
|
|
52
|
+
if (typeof id === "number") allocator.seedAbove(id);
|
|
53
|
+
}
|
|
54
|
+
if (node.type.name === "rangeAnchor") {
|
|
55
|
+
const attrs = readRangeAnchorAttrs(node);
|
|
56
|
+
if (attrs.ok && attrs.value.start.type === "commentRangeStart") allocator.seedAbove(attrs.value.start.id);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
max = Math.max(max, maxAnnotationIdInDoc(view.state.doc));
|
|
60
|
+
}
|
|
36
61
|
allocator.seedAbove(max);
|
|
37
62
|
seedRevisionIdsAbove(max);
|
|
38
63
|
}
|
|
39
64
|
//#endregion
|
|
40
|
-
export { PENDING_COMMENT_ID, createCommentIdAllocator, seedCommentAllocator };
|
|
65
|
+
export { CommentIdSpaceExhaustedError, PENDING_COMMENT_ID, allocateCommentId, createCommentIdAllocator, seedCommentAllocator, seedCommentIdAbove };
|
|
@@ -23,11 +23,6 @@ type ToProseDocOptions = {
|
|
|
23
23
|
* @param options - Conversion options including style definitions
|
|
24
24
|
*/
|
|
25
25
|
declare function toProseDoc(document: document_d_exports.Document, options?: ToProseDocOptions): Node;
|
|
26
|
-
/**
|
|
27
|
-
* Whether a cell carries content a vertical-merge continuation must not hide.
|
|
28
|
-
* The reader keeps such a continuation as a cell of its own.
|
|
29
|
-
*/
|
|
30
|
-
declare function tableCellHasMeaningfulContent(cell: document_d_exports.TableCell): boolean;
|
|
31
26
|
declare function standaloneTableCellToProseMirror(cell: document_d_exports.TableCell, nodeType: "tableCell" | "tableHeader"): Node;
|
|
32
27
|
/**
|
|
33
28
|
* Convert HeaderFooter content (array of Paragraph/Table blocks) to a ProseMirror document.
|
|
@@ -44,4 +39,4 @@ declare function footnoteToProseDoc(content: document_d_exports.BlockContent[],
|
|
|
44
39
|
*/
|
|
45
40
|
declare function createEmptyDoc(): Node;
|
|
46
41
|
//#endregion
|
|
47
|
-
export { ToProseDocOptions, createEmptyDoc, footnoteToProseDoc, headerFooterToProseDoc, standaloneTableCellToProseMirror,
|
|
42
|
+
export { ToProseDocOptions, createEmptyDoc, footnoteToProseDoc, headerFooterToProseDoc, standaloneTableCellToProseMirror, toProseDoc };
|
|
@@ -6,6 +6,7 @@ import { isBaselineVertAlign } from "../../docx/runParser.js";
|
|
|
6
6
|
import { replayableShapeAlternateContent } from "../../docx/shapeAlternateContent.js";
|
|
7
7
|
import { resolveTableLook } from "../../docx/tableLook.js";
|
|
8
8
|
import { isCellMergeContinuation } from "../../docx/tableParser.js";
|
|
9
|
+
import { calculateRowSpans } from "../../docx/verticalMergeProjection.js";
|
|
9
10
|
import { copiedWrapPolygon } from "../../docx/wrapPolygon.js";
|
|
10
11
|
import { buildPageBreakRunSourceDescendantIndex } from "../../internal/pageBreakRunSourceDescendantIndex.js";
|
|
11
12
|
import { createStyleEngine } from "../../style-engine/styleEngine.js";
|
|
@@ -893,122 +894,6 @@ function resolveTextFormatting(formatting, styleResolver) {
|
|
|
893
894
|
if (!styleResolver) return formatting;
|
|
894
895
|
return mergeTextFormatting(styleResolver.resolveRunStyle(formatting.styleId), formatting);
|
|
895
896
|
}
|
|
896
|
-
function calculateRowSpans(table) {
|
|
897
|
-
const result = /* @__PURE__ */ new Map();
|
|
898
|
-
const numRows = table.rows.length;
|
|
899
|
-
const activeMerges = /* @__PURE__ */ new Map();
|
|
900
|
-
for (let rowIndex = 0; rowIndex < numRows; rowIndex++) {
|
|
901
|
-
const row = table.rows[rowIndex];
|
|
902
|
-
if (row.cells.length === 0) {
|
|
903
|
-
clearActiveVerticalMerges(activeMerges, result);
|
|
904
|
-
continue;
|
|
905
|
-
}
|
|
906
|
-
let colIndex = row.formatting?.gridBefore ?? 0;
|
|
907
|
-
const rowCells = row.cells.map((cell) => {
|
|
908
|
-
const colspan = cell.formatting?.gridSpan ?? 1;
|
|
909
|
-
const vMerge = cell.formatting?.vMerge;
|
|
910
|
-
const isMergeContinuation = isCellMergeContinuation(cell);
|
|
911
|
-
const startRow = isMergeContinuation ? activeMerges.get(colIndex) : void 0;
|
|
912
|
-
const info = {
|
|
913
|
-
cell,
|
|
914
|
-
colIndex,
|
|
915
|
-
colspan,
|
|
916
|
-
vMerge,
|
|
917
|
-
isMergeContinuation,
|
|
918
|
-
startRow,
|
|
919
|
-
hasMeaningfulContent: tableCellHasMeaningfulContent(cell),
|
|
920
|
-
shouldSkip: isMergeContinuation && startRow !== void 0
|
|
921
|
-
};
|
|
922
|
-
colIndex += colspan;
|
|
923
|
-
return info;
|
|
924
|
-
});
|
|
925
|
-
const rowWouldBeEmpty = rowCells.length > 0 && rowCells.every((cell) => cell.shouldSkip);
|
|
926
|
-
for (const cellInfo of rowCells) {
|
|
927
|
-
const { colIndex: cellColIndex, vMerge, isMergeContinuation, startRow, hasMeaningfulContent } = cellInfo;
|
|
928
|
-
const key = `${rowIndex}-${cellColIndex}`;
|
|
929
|
-
if (vMerge === "restart") {
|
|
930
|
-
closeVerticalMerge(activeMerges, result, cellColIndex);
|
|
931
|
-
activeMerges.set(cellColIndex, rowIndex);
|
|
932
|
-
result.set(key, {
|
|
933
|
-
rowSpan: 1,
|
|
934
|
-
skip: false
|
|
935
|
-
});
|
|
936
|
-
} else if (isMergeContinuation) {
|
|
937
|
-
if (startRow === void 0 || rowWouldBeEmpty || hasMeaningfulContent) {
|
|
938
|
-
result.set(key, {
|
|
939
|
-
rowSpan: 1,
|
|
940
|
-
skip: false
|
|
941
|
-
});
|
|
942
|
-
if ((rowWouldBeEmpty || hasMeaningfulContent) && startRow !== void 0) {
|
|
943
|
-
const restartCell = result.get(`${startRow}-${cellColIndex}`);
|
|
944
|
-
if (restartCell) restartCell.preserveVMergeRestart = true;
|
|
945
|
-
activeMerges.delete(cellColIndex);
|
|
946
|
-
}
|
|
947
|
-
continue;
|
|
948
|
-
}
|
|
949
|
-
const startKey = `${startRow}-${cellColIndex}`;
|
|
950
|
-
const startCell = result.get(startKey);
|
|
951
|
-
if (startCell) {
|
|
952
|
-
startCell.rowSpan++;
|
|
953
|
-
startCell.continuationCells ??= [];
|
|
954
|
-
startCell.continuationCells.push(cellInfo.cell);
|
|
955
|
-
}
|
|
956
|
-
result.set(key, {
|
|
957
|
-
rowSpan: 1,
|
|
958
|
-
skip: true
|
|
959
|
-
});
|
|
960
|
-
} else {
|
|
961
|
-
closeVerticalMerge(activeMerges, result, cellColIndex);
|
|
962
|
-
result.set(key, {
|
|
963
|
-
rowSpan: 1,
|
|
964
|
-
skip: false
|
|
965
|
-
});
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
clearActiveVerticalMerges(activeMerges, result);
|
|
970
|
-
return result;
|
|
971
|
-
}
|
|
972
|
-
/**
|
|
973
|
-
* End the vertical merge active in one column.
|
|
974
|
-
*
|
|
975
|
-
* A `w:vMerge="restart"` is carried through the editor by its cell's rowspan,
|
|
976
|
-
* which only exists once a continuation joins it. A merge that closes with a
|
|
977
|
-
* rowspan of 1 — a restart with no continuation, one interrupted by a plain
|
|
978
|
-
* cell, one whose continuation is in a row a revision removed — has nothing
|
|
979
|
-
* but this flag to say the cell was a merge origin, and dropping it changes
|
|
980
|
-
* the table's visible structure.
|
|
981
|
-
*/
|
|
982
|
-
function closeVerticalMerge(activeMerges, result, colIndex) {
|
|
983
|
-
const startRow = activeMerges.get(colIndex);
|
|
984
|
-
if (startRow === void 0) return;
|
|
985
|
-
const restartCell = result.get(`${startRow}-${colIndex}`);
|
|
986
|
-
if (restartCell && restartCell.rowSpan === 1) restartCell.preserveVMergeRestart = true;
|
|
987
|
-
activeMerges.delete(colIndex);
|
|
988
|
-
}
|
|
989
|
-
function clearActiveVerticalMerges(activeMerges, result) {
|
|
990
|
-
for (const colIndex of [...activeMerges.keys()]) closeVerticalMerge(activeMerges, result, colIndex);
|
|
991
|
-
}
|
|
992
|
-
/**
|
|
993
|
-
* Whether a cell carries content a vertical-merge continuation must not hide.
|
|
994
|
-
* The reader keeps such a continuation as a cell of its own.
|
|
995
|
-
*/
|
|
996
|
-
function tableCellHasMeaningfulContent(cell) {
|
|
997
|
-
return cell.content.some(blockHasMeaningfulContent);
|
|
998
|
-
}
|
|
999
|
-
function blockHasMeaningfulContent(block) {
|
|
1000
|
-
if (block.type === "preservedBlock") return true;
|
|
1001
|
-
if (block.type === "bookmarkStart" || block.type === "bookmarkEnd") return true;
|
|
1002
|
-
if (block.type === "table") return block.rows.some((row) => row.cells.some((cell) => tableCellHasMeaningfulContent(cell)));
|
|
1003
|
-
if (block.type === "blockSdt" || block.type === "blockCustomXml") return true;
|
|
1004
|
-
return block.content.some(paragraphContentHasMeaningfulContent);
|
|
1005
|
-
}
|
|
1006
|
-
function paragraphContentHasMeaningfulContent(content) {
|
|
1007
|
-
if (content.type === "run") return content.content.length > 0;
|
|
1008
|
-
if (content.type === "hyperlink") return content.children.some(paragraphContentHasMeaningfulContent);
|
|
1009
|
-
if (content.type === "insertion" || content.type === "deletion" || content.type === "moveFrom" || content.type === "moveTo") return content.content.some(paragraphContentHasMeaningfulContent);
|
|
1010
|
-
return true;
|
|
1011
|
-
}
|
|
1012
897
|
function convertTable(table, styleResolver, context) {
|
|
1013
898
|
for (const row of table.rows) for (const cell of row.cells) {
|
|
1014
899
|
if (isCellMergeContinuation(cell) && context.pageBreakRunSourceDescendants.containsPageBreakRun(cell.content)) {
|
|
@@ -2840,4 +2725,4 @@ function createEmptyDoc() {
|
|
|
2840
2725
|
return schema.node("doc", null, [schema.node("paragraph", {}, [])]);
|
|
2841
2726
|
}
|
|
2842
2727
|
//#endregion
|
|
2843
|
-
export { createEmptyDoc, footnoteToProseDoc, headerFooterToProseDoc, standaloneTableCellToProseMirror,
|
|
2728
|
+
export { createEmptyDoc, footnoteToProseDoc, headerFooterToProseDoc, standaloneTableCellToProseMirror, toProseDoc };
|
|
@@ -1,5 +1,97 @@
|
|
|
1
1
|
import { createExtension } from "../create.js";
|
|
2
|
-
import {
|
|
2
|
+
import { Plugin } from "prosemirror-state";
|
|
3
|
+
import { closeHistory, history, isHistoryTransaction, redo, undo } from "prosemirror-history";
|
|
4
|
+
//#region src/prosemirror/extensions/core/HistoryExtension.ts
|
|
5
|
+
/**
|
|
6
|
+
* History Extension — undo/redo via prosemirror-history
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* History adjacency is based on step maps, which differ for physical deletions
|
|
10
|
+
* and suggestion marks. Group continuous typing and same-direction deletions explicitly; each
|
|
11
|
+
* structural edit, paste, drop, or composition starts its own undo event.
|
|
12
|
+
* Capture DOM input before the shared input router or a keymap handles it.
|
|
13
|
+
*/
|
|
14
|
+
const inputBoundaryPlugin = (newGroupDelay) => {
|
|
15
|
+
let previousKind;
|
|
16
|
+
let deletionGroup = 0;
|
|
17
|
+
let previousInputTime = 0;
|
|
18
|
+
let pendingDeletionGroup = null;
|
|
19
|
+
return new Plugin({
|
|
20
|
+
filterTransaction(tr) {
|
|
21
|
+
const pendingGroup = pendingDeletionGroup;
|
|
22
|
+
pendingDeletionGroup = null;
|
|
23
|
+
if (isHistoryTransaction(tr) || !tr.docChanged && tr.selectionSet) {
|
|
24
|
+
previousKind = void 0;
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
if (tr.docChanged && (previousKind === "deleteBackward" || previousKind === "deleteForward" || previousKind === "deletion")) {
|
|
28
|
+
if (pendingGroup !== null) tr.setMeta("composition", pendingGroup);
|
|
29
|
+
else if (!tr.getMeta("appendedTransaction")) previousKind = void 0;
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
},
|
|
33
|
+
view(view) {
|
|
34
|
+
const startInput = (kind) => {
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
if (kind !== previousKind || kind === "structure" || kind === "paste" || kind === "drop" || now - previousInputTime > newGroupDelay) {
|
|
37
|
+
deletionGroup--;
|
|
38
|
+
view.dispatch(closeHistory(view.state.tr));
|
|
39
|
+
}
|
|
40
|
+
previousInputTime = now;
|
|
41
|
+
previousKind = kind;
|
|
42
|
+
pendingDeletionGroup = kind === "deleteBackward" || kind === "deleteForward" || kind === "deletion" ? deletionGroup : null;
|
|
43
|
+
};
|
|
44
|
+
const keydown = (event) => {
|
|
45
|
+
if (event.isComposing) return;
|
|
46
|
+
if (event.key === "Backspace" || event.key === "Delete") {
|
|
47
|
+
startInput(event.key === "Backspace" ? "deleteBackward" : "deleteForward");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (event.key === "Enter") {
|
|
51
|
+
startInput("structure");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (event.key.length === 1 && !event.metaKey && !event.ctrlKey) startInput("typing");
|
|
55
|
+
};
|
|
56
|
+
const beforeinput = (event) => {
|
|
57
|
+
if (event.inputType === "insertCompositionText" || event.isComposing) {
|
|
58
|
+
startInput("composition");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (event.inputType === "insertText" || event.inputType === "insertReplacementText") {
|
|
62
|
+
startInput("typing");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (event.inputType.startsWith("delete")) if (event.inputType.endsWith("Backward")) startInput("deleteBackward");
|
|
66
|
+
else if (event.inputType.endsWith("Forward")) startInput("deleteForward");
|
|
67
|
+
else startInput("deletion");
|
|
68
|
+
else if (event.inputType === "insertParagraph" || event.inputType === "insertLineBreak") startInput("structure");
|
|
69
|
+
};
|
|
70
|
+
const paste = () => startInput("paste");
|
|
71
|
+
const drop = () => startInput("drop");
|
|
72
|
+
const compositionstart = () => {
|
|
73
|
+
view.dispatch(closeHistory(view.state.tr));
|
|
74
|
+
previousKind = "composition";
|
|
75
|
+
};
|
|
76
|
+
view.dom.addEventListener("keydown", keydown, true);
|
|
77
|
+
view.dom.addEventListener("beforeinput", beforeinput, true);
|
|
78
|
+
view.dom.addEventListener("paste", paste, true);
|
|
79
|
+
view.dom.addEventListener("drop", drop, true);
|
|
80
|
+
view.dom.addEventListener("compositionstart", compositionstart, true);
|
|
81
|
+
return { destroy() {
|
|
82
|
+
view.dom.removeEventListener("keydown", keydown, true);
|
|
83
|
+
view.dom.removeEventListener("beforeinput", beforeinput, true);
|
|
84
|
+
view.dom.removeEventListener("paste", paste, true);
|
|
85
|
+
view.dom.removeEventListener("drop", drop, true);
|
|
86
|
+
view.dom.removeEventListener("compositionstart", compositionstart, true);
|
|
87
|
+
} };
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
const consumeHistoryShortcut = (command) => (state, dispatch, view) => {
|
|
92
|
+
command(state, dispatch, view);
|
|
93
|
+
return true;
|
|
94
|
+
};
|
|
3
95
|
const HistoryExtension = createExtension({
|
|
4
96
|
name: "history",
|
|
5
97
|
defaultOptions: {
|
|
@@ -12,15 +104,15 @@ const HistoryExtension = createExtension({
|
|
|
12
104
|
plugins: [history({
|
|
13
105
|
depth: options.depth,
|
|
14
106
|
newGroupDelay: options.newGroupDelay
|
|
15
|
-
})],
|
|
107
|
+
}), inputBoundaryPlugin(options.newGroupDelay)],
|
|
16
108
|
commands: {
|
|
17
109
|
undo: () => undo,
|
|
18
110
|
redo: () => redo
|
|
19
111
|
},
|
|
20
112
|
...options.shortcuts === "editor" && { keyboardShortcuts: {
|
|
21
|
-
"Mod-z": undo,
|
|
22
|
-
"Mod-y": redo,
|
|
23
|
-
"Mod-Shift-z": redo
|
|
113
|
+
"Mod-z": consumeHistoryShortcut(undo),
|
|
114
|
+
"Mod-y": consumeHistoryShortcut(redo),
|
|
115
|
+
"Mod-Shift-z": consumeHistoryShortcut(redo)
|
|
24
116
|
} }
|
|
25
117
|
};
|
|
26
118
|
}
|