@stll/folio-core 0.1.3 → 0.3.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/dist/ai-edits/clean-text.d.ts +21 -1
- package/dist/ai-edits/clean-text.js +69 -1
- package/dist/ai-edits/headless.d.ts +49 -22
- package/dist/ai-edits/headless.js +101 -77
- package/dist/ai-edits/index.d.ts +3 -1
- package/dist/ai-edits/index.js +3 -1
- package/dist/ai-edits/read.d.ts +43 -0
- package/dist/ai-edits/read.js +91 -0
- package/dist/docx/index.d.ts +2 -0
- package/dist/docx/index.js +2 -0
- package/dist/docx/parser.js +1 -1
- package/dist/docx/wrapTypes.d.ts +8 -1
- package/dist/docx/wrapTypes.js +0 -8
- package/dist/i18n/messages/catalogs.gen.d.ts +9899 -0
- package/dist/i18n/messages/catalogs.gen.js +9831 -0
- package/dist/i18n/messages/messages.gen.d.ts +585 -0
- package/dist/i18n/messages/messages.gen.js +0 -0
- package/dist/i18n/messages.d.ts +13 -0
- package/dist/i18n/messages.js +39 -0
- package/dist/layout-bridge/cellSelectionHighlight.d.ts +18 -0
- package/dist/layout-bridge/cellSelectionHighlight.js +36 -0
- package/dist/layout-bridge/headerFooterLayout.d.ts +50 -0
- package/dist/layout-bridge/headerFooterLayout.js +216 -0
- package/dist/layout-bridge/measuring/measureBlocksPipeline.d.ts +33 -0
- package/dist/layout-bridge/measuring/measureBlocksPipeline.js +0 -0
- package/dist/layout-bridge/sectionColumns.d.ts +11 -0
- package/dist/layout-bridge/sectionColumns.js +21 -0
- package/dist/layout-bridge/tableInsertHover.d.ts +42 -0
- package/dist/layout-bridge/tableInsertHover.js +102 -0
- package/dist/layout-engine/index.d.ts +2 -1
- package/dist/layout-engine/index.js +2 -1
- package/dist/layout-engine/measure/measureParagraph.js +40 -5
- package/dist/layout-engine/pmPageIndex.d.ts +24 -0
- package/dist/layout-engine/pmPageIndex.js +37 -0
- package/dist/layout-painter/imageLayout.d.ts +101 -0
- package/dist/layout-painter/imageLayout.js +161 -0
- package/dist/layout-painter/renderPage.d.ts +16 -1
- package/dist/layout-painter/renderPage.js +28 -1
- package/dist/layout-painter/renderParagraph.js +1 -1
- package/dist/layout-painter/sdtBoundary.d.ts +24 -1
- package/dist/layout-painter/sdtBoundary.js +40 -1
- package/dist/managers/AutoSaveManager.d.ts +89 -0
- package/dist/managers/AutoSaveManager.js +279 -0
- package/dist/managers/ClipboardManager.d.ts +31 -0
- package/dist/managers/ClipboardManager.js +147 -0
- package/dist/paged-layout/rangeProjection.d.ts +23 -1
- package/dist/paged-layout/rangeProjection.js +34 -1
- package/dist/prosemirror/cellDragSelection.d.ts +34 -0
- package/dist/prosemirror/cellDragSelection.js +69 -0
- package/dist/prosemirror/commands/sectionBreak.d.ts +15 -0
- package/dist/prosemirror/commands/sectionBreak.js +54 -0
- package/dist/prosemirror/commentIdAllocator.d.ts +32 -0
- package/dist/prosemirror/commentIdAllocator.js +35 -0
- package/dist/prosemirror/commentOps.d.ts +35 -0
- package/dist/prosemirror/commentOps.js +104 -0
- package/dist/prosemirror/conversion/toProseDoc.js +29 -6
- package/dist/prosemirror/extensions/index.d.ts +3 -0
- package/dist/prosemirror/extensions/index.js +3 -0
- package/dist/prosemirror/imageCommit.d.ts +42 -0
- package/dist/prosemirror/imageCommit.js +118 -0
- package/dist/prosemirror/paraText.d.ts +26 -0
- package/dist/prosemirror/paraText.js +73 -0
- package/dist/prosemirror/plugins/templateDirectives.d.ts +21 -1
- package/dist/prosemirror/plugins/templateDirectives.js +40 -1
- package/dist/prosemirror/queries.d.ts +46 -0
- package/dist/prosemirror/queries.js +74 -0
- package/dist/prosemirror/styles/styleResolver.d.ts +43 -1
- package/dist/prosemirror/styles/styleResolver.js +34 -0
- package/dist/prosemirror/tableResize.d.ts +49 -0
- package/dist/prosemirror/tableResize.js +162 -0
- package/dist/prosemirror/utils/extractTrackedChanges.d.ts +106 -0
- package/dist/prosemirror/utils/extractTrackedChanges.js +379 -0
- package/dist/prosemirror/utils/visualLineNavigation.d.ts +27 -0
- package/dist/prosemirror/utils/visualLineNavigation.js +217 -0
- package/dist/server.d.ts +2 -1
- package/dist/style-engine/index.d.ts +2 -2
- package/dist/style-engine/styleEngine.d.ts +14 -1
- package/dist/style-engine/styleEngine.js +15 -0
- package/dist/utils/colorResolver.d.ts +8 -1
- package/dist/utils/colorResolver.js +12 -1
- package/dist/utils/findVerticalScrollParent.d.ts +20 -0
- package/dist/utils/findVerticalScrollParent.js +30 -0
- package/dist/utils/fontResolver.d.ts +22 -3
- package/dist/utils/fontResolver.js +216 -31
- package/package.json +10 -1
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { TextSelection } from "prosemirror-state";
|
|
2
|
+
//#region src/prosemirror/commands/sectionBreak.ts
|
|
3
|
+
/**
|
|
4
|
+
* Insert a section break at the current cursor position.
|
|
5
|
+
*
|
|
6
|
+
* In OOXML a section break is the `sectPr` carried by the *last* paragraph of a
|
|
7
|
+
* section. So we split the current paragraph at the cursor, mark the first half
|
|
8
|
+
* with `sectionBreakType` (it becomes the section end), and leave the cursor in
|
|
9
|
+
* the second half (the first paragraph of the new section). Content after the
|
|
10
|
+
* cursor therefore flows into the new section — onto a new page for `nextPage`,
|
|
11
|
+
* or in place for `continuous`.
|
|
12
|
+
*/
|
|
13
|
+
function insertSectionBreakAtCursor(breakType) {
|
|
14
|
+
return (state, dispatch) => {
|
|
15
|
+
const { schema } = state;
|
|
16
|
+
const paragraphType = schema.nodes["paragraph"];
|
|
17
|
+
if (!paragraphType) return false;
|
|
18
|
+
const { $from } = state.selection;
|
|
19
|
+
if (!($from.parent.isTextblock ? state.doc.resolve($from.before()).depth === 0 : $from.depth === 0)) return false;
|
|
20
|
+
if (dispatch) {
|
|
21
|
+
const tr = state.tr;
|
|
22
|
+
let cursorPos;
|
|
23
|
+
if ($from.parent.isTextblock) {
|
|
24
|
+
const paraPos = $from.before();
|
|
25
|
+
tr.split($from.pos);
|
|
26
|
+
const firstPara = tr.doc.nodeAt(paraPos);
|
|
27
|
+
if (firstPara) tr.setNodeMarkup(paraPos, void 0, {
|
|
28
|
+
...firstPara.attrs,
|
|
29
|
+
sectionBreakType: breakType
|
|
30
|
+
});
|
|
31
|
+
cursorPos = tr.mapping.map($from.pos);
|
|
32
|
+
} else {
|
|
33
|
+
const pos = $from.pos;
|
|
34
|
+
tr.insert(pos, paragraphType.create({ sectionBreakType: breakType }));
|
|
35
|
+
cursorPos = pos + 1;
|
|
36
|
+
}
|
|
37
|
+
tr.setSelection(TextSelection.create(tr.doc, cursorPos));
|
|
38
|
+
dispatch(tr.scrollIntoView());
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Insert a "next page" section break at the cursor — starts a new section on a
|
|
45
|
+
* new page.
|
|
46
|
+
*/
|
|
47
|
+
const insertSectionBreakNextPage = insertSectionBreakAtCursor("nextPage");
|
|
48
|
+
/**
|
|
49
|
+
* Insert a "continuous" section break at the cursor — starts a new section on
|
|
50
|
+
* the same page.
|
|
51
|
+
*/
|
|
52
|
+
const insertSectionBreakContinuous = insertSectionBreakAtCursor("continuous");
|
|
53
|
+
//#endregion
|
|
54
|
+
export { insertSectionBreakContinuous, insertSectionBreakNextPage };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Comment } from "../types/content.js";
|
|
2
|
+
import { EditorView } from "prosemirror-view";
|
|
3
|
+
|
|
4
|
+
//#region src/prosemirror/commentIdAllocator.d.ts
|
|
5
|
+
/** Sentinel ID for a comment that hasn't been persisted yet (anchored to selection). */
|
|
6
|
+
declare const PENDING_COMMENT_ID = -1;
|
|
7
|
+
type CommentIdAllocator = {
|
|
8
|
+
/** Allocate the next ID and advance the counter. */next(): number;
|
|
9
|
+
/**
|
|
10
|
+
* On document load, bump the counter above the highest ID found in the
|
|
11
|
+
* loaded comments and tracked-change marks so subsequent allocations don't
|
|
12
|
+
* collide with already-present IDs.
|
|
13
|
+
*/
|
|
14
|
+
seedAbove(maxId: number): void;
|
|
15
|
+
};
|
|
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
|
+
*/
|
|
21
|
+
declare function createCommentIdAllocator(): CommentIdAllocator;
|
|
22
|
+
/**
|
|
23
|
+
* Seed an allocator above every comment/revision ID currently in the document
|
|
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;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { CommentIdAllocator, PENDING_COMMENT_ID, createCommentIdAllocator, seedCommentAllocator };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region src/prosemirror/commentIdAllocator.ts
|
|
2
|
+
/** Sentinel ID for a comment that hasn't been persisted yet (anchored to selection). */
|
|
3
|
+
const PENDING_COMMENT_ID = -1;
|
|
4
|
+
/**
|
|
5
|
+
* Create an instance-scoped monotonic comment/revision ID allocator. IDs are
|
|
6
|
+
* never reused (deleting a comment does not free its ID), and the counter is
|
|
7
|
+
* private to this allocator — multiple editors get independent ID spaces.
|
|
8
|
+
*/
|
|
9
|
+
function createCommentIdAllocator() {
|
|
10
|
+
let nextId = 1;
|
|
11
|
+
return {
|
|
12
|
+
next: () => nextId++,
|
|
13
|
+
seedAbove(maxId) {
|
|
14
|
+
if (maxId >= nextId) nextId = maxId + 1;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Seed an allocator above every comment/revision ID currently in the document
|
|
20
|
+
* — comment objects (including replies, which carry no mark) plus
|
|
21
|
+
* tracked-change `revisionId` marks. Because `seedAbove` only ever raises the
|
|
22
|
+
* counter, this is safe to call on load (React) or before each allocation
|
|
23
|
+
* (Vue): new IDs never collide with or reuse an existing one, and the comment
|
|
24
|
+
* and revision ID spaces stay unified.
|
|
25
|
+
*/
|
|
26
|
+
function seedCommentAllocator(allocator, comments, view) {
|
|
27
|
+
let max = 0;
|
|
28
|
+
for (const comment of comments ?? []) max = Math.max(max, comment.id);
|
|
29
|
+
if (view) view.state.doc.descendants((node) => {
|
|
30
|
+
for (const mark of node.marks) if (mark.attrs["revisionId"] != null) max = Math.max(max, mark.attrs["revisionId"]);
|
|
31
|
+
});
|
|
32
|
+
allocator.seedAbove(max);
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { PENDING_COMMENT_ID, createCommentIdAllocator, seedCommentAllocator };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Comment } from "../types/content.js";
|
|
2
|
+
import { CommentIdAllocator } from "./commentIdAllocator.js";
|
|
3
|
+
import { EditorView } from "prosemirror-view";
|
|
4
|
+
|
|
5
|
+
//#region src/prosemirror/commentOps.d.ts
|
|
6
|
+
/** Build a Comment object with a freshly-allocated ID. */
|
|
7
|
+
declare function createComment(allocator: CommentIdAllocator, text: string, authorName: string, parentId?: number): Comment;
|
|
8
|
+
type AddCommentOptions = {
|
|
9
|
+
paraId: string;
|
|
10
|
+
text: string;
|
|
11
|
+
author: string;
|
|
12
|
+
search?: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Locate the comment range (paragraph, narrowed by `search`), add the comment
|
|
16
|
+
* mark, dispatch, and return the created Comment. Returns null when the schema
|
|
17
|
+
* lacks a comment mark or the range can't be resolved. The caller owns adding
|
|
18
|
+
* the comment to its own state and showing the sidebar.
|
|
19
|
+
*/
|
|
20
|
+
declare function addCommentToRange(view: EditorView, options: AddCommentOptions, allocator: CommentIdAllocator): Comment | null;
|
|
21
|
+
type ProposeChangeOptions = {
|
|
22
|
+
paraId: string;
|
|
23
|
+
search: string;
|
|
24
|
+
replaceWith: string;
|
|
25
|
+
author: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Apply a tracked change (insertion / deletion / replace) to the located range.
|
|
29
|
+
* `search === ''` means a pure insertion at the paragraph end; `replaceWith ===
|
|
30
|
+
* ''` means a pure deletion. Refuses to layer onto an existing tracked change.
|
|
31
|
+
* Returns true when a change was dispatched, false otherwise.
|
|
32
|
+
*/
|
|
33
|
+
declare function applyProposedChange(view: EditorView, options: ProposeChangeOptions, allocator: CommentIdAllocator): boolean;
|
|
34
|
+
//#endregion
|
|
35
|
+
export { AddCommentOptions, ProposeChangeOptions, addCommentToRange, applyProposedChange, createComment };
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { findParagraphByParaId } from "./utils/findParagraphByParaId.js";
|
|
2
|
+
import { findTextInPmParagraph } from "./paraText.js";
|
|
3
|
+
//#region src/prosemirror/commentOps.ts
|
|
4
|
+
/** Build a Comment object with a freshly-allocated ID. */
|
|
5
|
+
function createComment(allocator, text, authorName, parentId) {
|
|
6
|
+
return {
|
|
7
|
+
id: allocator.next(),
|
|
8
|
+
author: authorName,
|
|
9
|
+
date: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10
|
+
content: [{
|
|
11
|
+
type: "paragraph",
|
|
12
|
+
formatting: {},
|
|
13
|
+
content: [{
|
|
14
|
+
type: "run",
|
|
15
|
+
formatting: {},
|
|
16
|
+
content: [{
|
|
17
|
+
type: "text",
|
|
18
|
+
text
|
|
19
|
+
}]
|
|
20
|
+
}]
|
|
21
|
+
}],
|
|
22
|
+
...parentId !== void 0 && { parentId }
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Locate the comment range (paragraph, narrowed by `search`), add the comment
|
|
27
|
+
* mark, dispatch, and return the created Comment. Returns null when the schema
|
|
28
|
+
* lacks a comment mark or the range can't be resolved. The caller owns adding
|
|
29
|
+
* the comment to its own state and showing the sidebar.
|
|
30
|
+
*/
|
|
31
|
+
function addCommentToRange(view, options, allocator) {
|
|
32
|
+
const { schema } = view.state;
|
|
33
|
+
if (!schema.marks["comment"]) return null;
|
|
34
|
+
const range = findParagraphByParaId(view.state.doc, options.paraId);
|
|
35
|
+
if (!range) return null;
|
|
36
|
+
let from = range.from + 1;
|
|
37
|
+
let to = range.to - 1;
|
|
38
|
+
if (options.search) {
|
|
39
|
+
const textRange = findTextInPmParagraph(view.state.doc, range.from, range.to, options.search);
|
|
40
|
+
if (!textRange) return null;
|
|
41
|
+
from = textRange.from;
|
|
42
|
+
to = textRange.to;
|
|
43
|
+
}
|
|
44
|
+
if (from >= to) return null;
|
|
45
|
+
const comment = createComment(allocator, options.text, options.author);
|
|
46
|
+
view.dispatch(view.state.tr.addMark(from, to, schema.marks["comment"].create({ commentId: comment.id })));
|
|
47
|
+
return comment;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Apply a tracked change (insertion / deletion / replace) to the located range.
|
|
51
|
+
* `search === ''` means a pure insertion at the paragraph end; `replaceWith ===
|
|
52
|
+
* ''` means a pure deletion. Refuses to layer onto an existing tracked change.
|
|
53
|
+
* Returns true when a change was dispatched, false otherwise.
|
|
54
|
+
*/
|
|
55
|
+
function applyProposedChange(view, options, allocator) {
|
|
56
|
+
const { schema } = view.state;
|
|
57
|
+
if (!schema.marks["deletion"] || !schema.marks["insertion"]) return false;
|
|
58
|
+
const range = findParagraphByParaId(view.state.doc, options.paraId);
|
|
59
|
+
if (!range) return false;
|
|
60
|
+
const isInsertion = options.search === "";
|
|
61
|
+
const isDeletion = options.replaceWith === "";
|
|
62
|
+
if (isInsertion && isDeletion) return false;
|
|
63
|
+
let textFrom;
|
|
64
|
+
let textTo;
|
|
65
|
+
if (isInsertion) {
|
|
66
|
+
textFrom = range.to - 1;
|
|
67
|
+
textTo = range.to - 1;
|
|
68
|
+
} else {
|
|
69
|
+
const textRange = findTextInPmParagraph(view.state.doc, range.from, range.to, options.search);
|
|
70
|
+
if (!textRange) return false;
|
|
71
|
+
textFrom = textRange.from;
|
|
72
|
+
textTo = textRange.to;
|
|
73
|
+
}
|
|
74
|
+
if (textFrom < textTo) {
|
|
75
|
+
let overlaps = false;
|
|
76
|
+
view.state.doc.nodesBetween(textFrom, textTo, (node) => {
|
|
77
|
+
for (const m of node.marks) if (m.type === schema.marks["insertion"] || m.type === schema.marks["deletion"]) {
|
|
78
|
+
overlaps = true;
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
return true;
|
|
82
|
+
});
|
|
83
|
+
if (overlaps) return false;
|
|
84
|
+
}
|
|
85
|
+
const revisionId = allocator.next();
|
|
86
|
+
const date = (/* @__PURE__ */ new Date()).toISOString();
|
|
87
|
+
const deletionMark = schema.marks["deletion"].create({
|
|
88
|
+
revisionId,
|
|
89
|
+
author: options.author,
|
|
90
|
+
date
|
|
91
|
+
});
|
|
92
|
+
const insertionMark = schema.marks["insertion"].create({
|
|
93
|
+
revisionId,
|
|
94
|
+
author: options.author,
|
|
95
|
+
date
|
|
96
|
+
});
|
|
97
|
+
let tr = view.state.tr;
|
|
98
|
+
if (!isInsertion) tr = tr.addMark(textFrom, textTo, deletionMark);
|
|
99
|
+
if (!isDeletion) tr = tr.insert(textTo, schema.text(options.replaceWith, [insertionMark]));
|
|
100
|
+
view.dispatch(tr);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
//#endregion
|
|
104
|
+
export { addCommentToRange, applyProposedChange, createComment };
|
|
@@ -5,6 +5,7 @@ import { setAutospacingBaseValue } from "../autospacingBase.js";
|
|
|
5
5
|
import { shadingToRunShadingAttrs } from "./runShadingMark.js";
|
|
6
6
|
import { assertValidProseMirrorDocument } from "../validation.js";
|
|
7
7
|
import { mergeTextFormatting } from "../../utils/textFormattingMerge.js";
|
|
8
|
+
import { mergeParagraphFormatting } from "../../utils/paragraphFormattingMerge.js";
|
|
8
9
|
import { marksToTextFormatting } from "./fromProseDoc.js";
|
|
9
10
|
import { createStyleEngine } from "../../style-engine/styleEngine.js";
|
|
10
11
|
import { buildRunFormattingOverrideAttrs } from "../extensions/marks/RunFormattingOverrideExtension.js";
|
|
@@ -77,8 +78,8 @@ function convertBlockSdt(blockSdt, convertBlocks) {
|
|
|
77
78
|
* Resolves style-based text formatting and passes it to runs so that
|
|
78
79
|
* paragraph styles (like Heading1) apply their font size, color, etc.
|
|
79
80
|
*/
|
|
80
|
-
function convertParagraph(paragraph, styleResolver, activeCommentIds, extraRunFormatting) {
|
|
81
|
-
const attrs = paragraphFormattingToAttrs(paragraph, styleResolver);
|
|
81
|
+
function convertParagraph(paragraph, styleResolver, activeCommentIds, extraRunFormatting, tableParagraphOverlay) {
|
|
82
|
+
const attrs = paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOverlay);
|
|
82
83
|
const inlineNodes = [];
|
|
83
84
|
let inlineOffset = 0;
|
|
84
85
|
let bookmarksArr;
|
|
@@ -202,7 +203,7 @@ function canCarryTrackedRunMark(node, markType) {
|
|
|
202
203
|
* If a styleResolver is provided, resolves style-based formatting and merges
|
|
203
204
|
* with inline formatting. Inline formatting takes precedence.
|
|
204
205
|
*/
|
|
205
|
-
function paragraphFormattingToAttrs(paragraph, styleResolver) {
|
|
206
|
+
function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOverlay) {
|
|
206
207
|
const formatting = paragraph.formatting;
|
|
207
208
|
const styleId = formatting?.styleId;
|
|
208
209
|
const attrs = {};
|
|
@@ -233,7 +234,7 @@ function paragraphFormattingToAttrs(paragraph, styleResolver) {
|
|
|
233
234
|
};
|
|
234
235
|
let stylePpr;
|
|
235
236
|
if (styleResolver) {
|
|
236
|
-
stylePpr = styleResolver.
|
|
237
|
+
stylePpr = styleResolver.resolveParagraphStyleInTable(styleId, tableParagraphOverlay).paragraphFormatting;
|
|
237
238
|
set("alignment", formatting?.alignment ?? stylePpr?.alignment);
|
|
238
239
|
set("spaceBefore", formatting?.spaceBefore ?? stylePpr?.spaceBefore);
|
|
239
240
|
set("spaceAfter", formatting?.spaceAfter ?? stylePpr?.spaceAfter);
|
|
@@ -299,6 +300,22 @@ function paragraphFormattingToAttrs(paragraph, styleResolver) {
|
|
|
299
300
|
return attrs;
|
|
300
301
|
}
|
|
301
302
|
/**
|
|
303
|
+
* Pick the paragraph-spacing fields out of a table style's (or conditional
|
|
304
|
+
* region's) `w:pPr` for use as the cell-paragraph cascade overlay. Narrower
|
|
305
|
+
* than the full `ParagraphFormatting` bag — see
|
|
306
|
+
* {@link TableCellParagraphSpacingOverlay}.
|
|
307
|
+
*/
|
|
308
|
+
function extractTableParagraphSpacingOverlay(pPr) {
|
|
309
|
+
if (!pPr) return;
|
|
310
|
+
const overlay = {};
|
|
311
|
+
if (pPr.spaceBefore !== void 0) overlay.spaceBefore = pPr.spaceBefore;
|
|
312
|
+
if (pPr.spaceAfter !== void 0) overlay.spaceAfter = pPr.spaceAfter;
|
|
313
|
+
if (pPr.lineSpacing !== void 0) overlay.lineSpacing = pPr.lineSpacing;
|
|
314
|
+
if (pPr.lineSpacingRule !== void 0) overlay.lineSpacingRule = pPr.lineSpacingRule;
|
|
315
|
+
if (pPr.contextualSpacing !== void 0) overlay.contextualSpacing = pPr.contextualSpacing;
|
|
316
|
+
return Object.keys(overlay).length > 0 ? overlay : void 0;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
302
319
|
* Resolve table style conditional formatting
|
|
303
320
|
*/
|
|
304
321
|
function resolveTableStyleConditional(styleResolver, tableStyleId, conditionType) {
|
|
@@ -308,9 +325,11 @@ function resolveTableStyleConditional(styleResolver, tableStyleId, conditionType
|
|
|
308
325
|
const conditional = style.tblStylePr.find((p) => p.type === conditionType);
|
|
309
326
|
if (!conditional) return;
|
|
310
327
|
const mergedRunProps = mergeTextFormatting(conditional.pPr?.runProperties ? resolveTextFormatting(conditional.pPr.runProperties, styleResolver) : void 0, conditional.rPr ? resolveTextFormatting(conditional.rPr, styleResolver) : void 0);
|
|
328
|
+
const paragraphSpacingOverlay = extractTableParagraphSpacingOverlay(conditional.pPr);
|
|
311
329
|
const result = {};
|
|
312
330
|
if (conditional.tcPr) result.tcPr = conditional.tcPr;
|
|
313
331
|
if (mergedRunProps) result.rPr = mergedRunProps;
|
|
332
|
+
if (paragraphSpacingOverlay) result.pPr = paragraphSpacingOverlay;
|
|
314
333
|
return result;
|
|
315
334
|
}
|
|
316
335
|
function resolveTableBaseStyle(styleResolver, tableStyleId) {
|
|
@@ -318,10 +337,12 @@ function resolveTableBaseStyle(styleResolver, tableStyleId) {
|
|
|
318
337
|
const style = styleResolver.getStyle(tableStyleId);
|
|
319
338
|
if (!style) return;
|
|
320
339
|
const mergedRunProps = mergeTextFormatting(style.pPr?.runProperties ? resolveTextFormatting(style.pPr.runProperties, styleResolver) : void 0, style.rPr ? resolveTextFormatting(style.rPr, styleResolver) : void 0);
|
|
340
|
+
const paragraphSpacingOverlay = extractTableParagraphSpacingOverlay(style.pPr);
|
|
321
341
|
const result = {};
|
|
322
342
|
if (style.tcPr) result.tcPr = style.tcPr;
|
|
323
343
|
if (mergedRunProps) result.rPr = mergedRunProps;
|
|
324
|
-
|
|
344
|
+
if (paragraphSpacingOverlay) result.pPr = paragraphSpacingOverlay;
|
|
345
|
+
return result.tcPr || result.rPr || result.pPr ? result : void 0;
|
|
325
346
|
}
|
|
326
347
|
function mergeConditionalStyles(base, override) {
|
|
327
348
|
if (!base && !override) return;
|
|
@@ -351,6 +372,8 @@ function mergeConditionalStyles(base, override) {
|
|
|
351
372
|
}
|
|
352
373
|
const mergedRPr = mergeTextFormatting(base.rPr, override.rPr);
|
|
353
374
|
if (mergedRPr) merged.rPr = mergedRPr;
|
|
375
|
+
const mergedPPr = mergeParagraphFormatting(base.pPr, override.pPr);
|
|
376
|
+
if (mergedPPr) merged.pPr = mergedPPr;
|
|
354
377
|
return merged;
|
|
355
378
|
}
|
|
356
379
|
function hasDirectRunFormatting(formatting) {
|
|
@@ -725,7 +748,7 @@ function convertTableCell(cell, styleResolver, isHeader, gridWidthPercent, condi
|
|
|
725
748
|
if (preserveVMergeRestart) attrs._preserveVMergeRestart = true;
|
|
726
749
|
if (vMergeContinuationCells && vMergeContinuationCells.length > 0) attrs._docxVMergeContinuationCells = vMergeContinuationCells;
|
|
727
750
|
const contentNodes = [];
|
|
728
|
-
for (const content of cell.content) if (content.type === "paragraph") contentNodes.push(convertParagraph(content, styleResolver, void 0, conditionalStyle?.rPr));
|
|
751
|
+
for (const content of cell.content) if (content.type === "paragraph") contentNodes.push(convertParagraph(content, styleResolver, void 0, conditionalStyle?.rPr, conditionalStyle?.pPr));
|
|
729
752
|
else contentNodes.push(convertTable(content, styleResolver, theme));
|
|
730
753
|
if (contentNodes.length === 0) contentNodes.push(schema.node("paragraph", {}, []));
|
|
731
754
|
const nodeType = isHeader ? "tableHeader" : "tableCell";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Node } from "prosemirror-model";
|
|
2
|
+
import { EditorView } from "prosemirror-view";
|
|
3
|
+
|
|
4
|
+
//#region src/prosemirror/imageCommit.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* An image resize handle: the 4 corners ('nw'/'ne'/'se'/'sw') resize both axes
|
|
7
|
+
* and keep the aspect ratio; the 4 edge midpoints ('n'/'s'/'e'/'w') resize a
|
|
8
|
+
* single axis so the user can deliberately stretch the image (break aspect).
|
|
9
|
+
*/
|
|
10
|
+
type ImageResizeHandle = "nw" | "ne" | "se" | "sw" | "n" | "s" | "e" | "w";
|
|
11
|
+
/**
|
|
12
|
+
* New image dimensions for a resize drag, shared by the React and Vue overlays
|
|
13
|
+
* (issue #266). Corner handles drive both axes (aspect-locked unless
|
|
14
|
+
* `lockAspect` is false, e.g. Shift held); edge handles drive one axis and
|
|
15
|
+
* never lock. The non-driven axis is returned unchanged. Driven axes are
|
|
16
|
+
* clamped to a sane pixel range.
|
|
17
|
+
*/
|
|
18
|
+
declare function calculateResizedImageDimensions(handle: ImageResizeHandle, deltaX: number, deltaY: number, startWidth: number, startHeight: number, lockAspect: boolean): {
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
};
|
|
22
|
+
/** True when the image is floating (anchored) rather than inline. */
|
|
23
|
+
declare function isFloatingImage(node: Node): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Resize commit: set the image node's `width`/`height`. Returns `pmPos` to
|
|
26
|
+
* re-select, or null if the position no longer holds an image.
|
|
27
|
+
*/
|
|
28
|
+
declare function commitImageResize(view: EditorView, pmPos: number, newWidth: number, newHeight: number): number | null;
|
|
29
|
+
/**
|
|
30
|
+
* Floating drag commit: rewrite the anchor's margin-relative `position`
|
|
31
|
+
* offsets (in EMU) so the image lands at the drop point while staying
|
|
32
|
+
* floating. Returns `pmPos` to re-select, or null on no-op / failure.
|
|
33
|
+
*/
|
|
34
|
+
declare function commitImageFloatMove(view: EditorView, pmPos: number, hOffsetEmu: number, vOffsetEmu: number): number | null;
|
|
35
|
+
/**
|
|
36
|
+
* Inline drag commit: move the image node to `dropPos` via a delete + insert
|
|
37
|
+
* pair. Returns the PM position to re-select, or null when the drop is a
|
|
38
|
+
* no-op (same slot) or fails.
|
|
39
|
+
*/
|
|
40
|
+
declare function commitImageInlineMove(view: EditorView, pmPos: number, dropPos: number): number | null;
|
|
41
|
+
//#endregion
|
|
42
|
+
export { ImageResizeHandle, calculateResizedImageDimensions, commitImageFloatMove, commitImageInlineMove, commitImageResize, isFloatingImage };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
//#region src/prosemirror/imageCommit.ts
|
|
2
|
+
const MIN_IMAGE_PX = 20;
|
|
3
|
+
const MAX_IMAGE_PX = 2e3;
|
|
4
|
+
/**
|
|
5
|
+
* New image dimensions for a resize drag, shared by the React and Vue overlays
|
|
6
|
+
* (issue #266). Corner handles drive both axes (aspect-locked unless
|
|
7
|
+
* `lockAspect` is false, e.g. Shift held); edge handles drive one axis and
|
|
8
|
+
* never lock. The non-driven axis is returned unchanged. Driven axes are
|
|
9
|
+
* clamped to a sane pixel range.
|
|
10
|
+
*/
|
|
11
|
+
function calculateResizedImageDimensions(handle, deltaX, deltaY, startWidth, startHeight, lockAspect) {
|
|
12
|
+
const drivesWidth = handle.includes("w") || handle.includes("e");
|
|
13
|
+
const drivesHeight = handle.includes("n") || handle.includes("s");
|
|
14
|
+
const isCorner = drivesWidth && drivesHeight;
|
|
15
|
+
const signX = handle.includes("w") ? -1 : 1;
|
|
16
|
+
const signY = handle.includes("n") ? -1 : 1;
|
|
17
|
+
let newWidth = drivesWidth ? startWidth + deltaX * signX : startWidth;
|
|
18
|
+
let newHeight = drivesHeight ? startHeight + deltaY * signY : startHeight;
|
|
19
|
+
if (isCorner && lockAspect) {
|
|
20
|
+
const scale = Math.max(newWidth / startWidth, newHeight / startHeight);
|
|
21
|
+
newWidth = startWidth * scale;
|
|
22
|
+
newHeight = startHeight * scale;
|
|
23
|
+
}
|
|
24
|
+
const clamp = (n) => Math.max(MIN_IMAGE_PX, Math.min(MAX_IMAGE_PX, n));
|
|
25
|
+
return {
|
|
26
|
+
width: drivesWidth ? clamp(newWidth) : startWidth,
|
|
27
|
+
height: drivesHeight ? clamp(newHeight) : startHeight
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** True when the image is floating (anchored) rather than inline. */
|
|
31
|
+
function isFloatingImage(node) {
|
|
32
|
+
const wrapType = node.attrs["wrapType"];
|
|
33
|
+
return node.attrs["displayMode"] === "float" || (wrapType ? [
|
|
34
|
+
"square",
|
|
35
|
+
"tight",
|
|
36
|
+
"through"
|
|
37
|
+
].includes(wrapType) : false);
|
|
38
|
+
}
|
|
39
|
+
/** Resolve the image node at `pmPos`, or null if it isn't an image. */
|
|
40
|
+
function imageNodeAt(view, pmPos) {
|
|
41
|
+
const node = view.state.doc.nodeAt(pmPos);
|
|
42
|
+
if (!node || node.type.name !== "image") return null;
|
|
43
|
+
return node;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resize commit: set the image node's `width`/`height`. Returns `pmPos` to
|
|
47
|
+
* re-select, or null if the position no longer holds an image.
|
|
48
|
+
*/
|
|
49
|
+
function commitImageResize(view, pmPos, newWidth, newHeight) {
|
|
50
|
+
try {
|
|
51
|
+
const node = imageNodeAt(view, pmPos);
|
|
52
|
+
if (!node) return null;
|
|
53
|
+
view.dispatch(view.state.tr.setNodeMarkup(pmPos, void 0, {
|
|
54
|
+
...node.attrs,
|
|
55
|
+
width: newWidth,
|
|
56
|
+
height: newHeight
|
|
57
|
+
}));
|
|
58
|
+
return pmPos;
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Floating drag commit: rewrite the anchor's margin-relative `position`
|
|
65
|
+
* offsets (in EMU) so the image lands at the drop point while staying
|
|
66
|
+
* floating. Returns `pmPos` to re-select, or null on no-op / failure.
|
|
67
|
+
*/
|
|
68
|
+
function commitImageFloatMove(view, pmPos, hOffsetEmu, vOffsetEmu) {
|
|
69
|
+
try {
|
|
70
|
+
const node = imageNodeAt(view, pmPos);
|
|
71
|
+
if (!node) return null;
|
|
72
|
+
const newPosition = {
|
|
73
|
+
horizontal: {
|
|
74
|
+
posOffset: hOffsetEmu,
|
|
75
|
+
relativeTo: "margin"
|
|
76
|
+
},
|
|
77
|
+
vertical: {
|
|
78
|
+
posOffset: vOffsetEmu,
|
|
79
|
+
relativeTo: "margin"
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
view.dispatch(view.state.tr.setNodeMarkup(pmPos, void 0, {
|
|
83
|
+
...node.attrs,
|
|
84
|
+
position: newPosition
|
|
85
|
+
}));
|
|
86
|
+
return pmPos;
|
|
87
|
+
} catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Inline drag commit: move the image node to `dropPos` via a delete + insert
|
|
93
|
+
* pair. Returns the PM position to re-select, or null when the drop is a
|
|
94
|
+
* no-op (same slot) or fails.
|
|
95
|
+
*/
|
|
96
|
+
function commitImageInlineMove(view, pmPos, dropPos) {
|
|
97
|
+
try {
|
|
98
|
+
const node = imageNodeAt(view, pmPos);
|
|
99
|
+
if (!node) return null;
|
|
100
|
+
if (dropPos === pmPos || dropPos === pmPos + 1) return null;
|
|
101
|
+
let tr = view.state.tr;
|
|
102
|
+
if (dropPos <= pmPos) {
|
|
103
|
+
tr = tr.delete(pmPos, pmPos + node.nodeSize);
|
|
104
|
+
tr = tr.insert(dropPos, node);
|
|
105
|
+
view.dispatch(tr);
|
|
106
|
+
return dropPos;
|
|
107
|
+
}
|
|
108
|
+
tr = tr.delete(pmPos, pmPos + node.nodeSize);
|
|
109
|
+
const adjusted = Math.min(dropPos - node.nodeSize, tr.doc.content.size);
|
|
110
|
+
tr = tr.insert(adjusted, node);
|
|
111
|
+
view.dispatch(tr);
|
|
112
|
+
return Math.min(adjusted, view.state.doc.content.size - 1);
|
|
113
|
+
} catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
export { calculateResizedImageDimensions, commitImageFloatMove, commitImageInlineMove, commitImageResize, isFloatingImage };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Node } from "prosemirror-model";
|
|
2
|
+
|
|
3
|
+
//#region src/prosemirror/paraText.d.ts
|
|
4
|
+
/** Text of a single PM node (typically a paragraph), vanilla view. */
|
|
5
|
+
declare function getVanillaNodeText(node: Node): string;
|
|
6
|
+
/** Text between two doc positions, vanilla view. */
|
|
7
|
+
declare function getVanillaTextBetween(doc: Node, from: number, to: number): string;
|
|
8
|
+
/**
|
|
9
|
+
* Find `searchText` within a PM paragraph range and return its position.
|
|
10
|
+
*
|
|
11
|
+
* Returns null if:
|
|
12
|
+
* - searchText is empty
|
|
13
|
+
* - searchText is not found
|
|
14
|
+
* - searchText appears more than once (ambiguous; caller disambiguates)
|
|
15
|
+
*
|
|
16
|
+
* The fullText is built from PM text nodes only and matches the vanilla
|
|
17
|
+
* view the agent reads via `read_document`: tracked insertions are
|
|
18
|
+
* excluded (not in the doc yet), tracked deletions are included (still
|
|
19
|
+
* in the doc until accepted), and comment markers are stripped.
|
|
20
|
+
*/
|
|
21
|
+
declare function findTextInPmParagraph(doc: Node, paragraphFrom: number, paragraphTo: number, searchText: string): {
|
|
22
|
+
from: number;
|
|
23
|
+
to: number;
|
|
24
|
+
} | null;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { findTextInPmParagraph, getVanillaNodeText, getVanillaTextBetween };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
//#region src/prosemirror/paraText.ts
|
|
2
|
+
/** Text of a single PM node (typically a paragraph), vanilla view. */
|
|
3
|
+
function getVanillaNodeText(node) {
|
|
4
|
+
const parts = [];
|
|
5
|
+
node.descendants((child) => {
|
|
6
|
+
if (!child.isText || !child.text) return true;
|
|
7
|
+
if (child.marks.some((m) => m.type.name === "insertion")) return false;
|
|
8
|
+
parts.push(child.text);
|
|
9
|
+
return true;
|
|
10
|
+
});
|
|
11
|
+
return parts.join("");
|
|
12
|
+
}
|
|
13
|
+
/** Text between two doc positions, vanilla view. */
|
|
14
|
+
function getVanillaTextBetween(doc, from, to) {
|
|
15
|
+
if (from >= to) return "";
|
|
16
|
+
const parts = [];
|
|
17
|
+
doc.nodesBetween(from, to, (child, pos) => {
|
|
18
|
+
if (!child.isText || !child.text) return;
|
|
19
|
+
if (child.marks.some((m) => m.type.name === "insertion")) return;
|
|
20
|
+
const start = Math.max(from, pos);
|
|
21
|
+
const end = Math.min(to, pos + child.text.length);
|
|
22
|
+
if (start < end) parts.push(child.text.slice(start - pos, end - pos));
|
|
23
|
+
});
|
|
24
|
+
return parts.join("");
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Find `searchText` within a PM paragraph range and return its position.
|
|
28
|
+
*
|
|
29
|
+
* Returns null if:
|
|
30
|
+
* - searchText is empty
|
|
31
|
+
* - searchText is not found
|
|
32
|
+
* - searchText appears more than once (ambiguous; caller disambiguates)
|
|
33
|
+
*
|
|
34
|
+
* The fullText is built from PM text nodes only and matches the vanilla
|
|
35
|
+
* view the agent reads via `read_document`: tracked insertions are
|
|
36
|
+
* excluded (not in the doc yet), tracked deletions are included (still
|
|
37
|
+
* in the doc until accepted), and comment markers are stripped.
|
|
38
|
+
*/
|
|
39
|
+
function findTextInPmParagraph(doc, paragraphFrom, paragraphTo, searchText) {
|
|
40
|
+
if (!searchText) return null;
|
|
41
|
+
let fullText = "";
|
|
42
|
+
const textPositions = [];
|
|
43
|
+
doc.nodesBetween(paragraphFrom, paragraphTo, (node, pos) => {
|
|
44
|
+
if (!node.isText || !node.text) return;
|
|
45
|
+
if (node.marks.some((m) => m.type.name === "insertion")) return;
|
|
46
|
+
textPositions.push({
|
|
47
|
+
pos,
|
|
48
|
+
len: node.text.length
|
|
49
|
+
});
|
|
50
|
+
fullText += node.text;
|
|
51
|
+
});
|
|
52
|
+
const firstMatch = fullText.indexOf(searchText);
|
|
53
|
+
if (firstMatch === -1) return null;
|
|
54
|
+
if (fullText.indexOf(searchText, firstMatch + 1) !== -1) return null;
|
|
55
|
+
let charOffset = 0;
|
|
56
|
+
let fromPos = paragraphFrom;
|
|
57
|
+
let toPos = paragraphFrom;
|
|
58
|
+
for (const tp of textPositions) {
|
|
59
|
+
const segEnd = charOffset + tp.len;
|
|
60
|
+
if (charOffset <= firstMatch && firstMatch < segEnd) fromPos = tp.pos + (firstMatch - charOffset);
|
|
61
|
+
if (charOffset <= firstMatch + searchText.length && firstMatch + searchText.length <= segEnd) {
|
|
62
|
+
toPos = tp.pos + (firstMatch + searchText.length - charOffset);
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
charOffset = segEnd;
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
from: fromPos,
|
|
69
|
+
to: toPos
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
export { findTextInPmParagraph, getVanillaNodeText, getVanillaTextBetween };
|
|
@@ -12,6 +12,26 @@ type DirectiveRange = {
|
|
|
12
12
|
clauseVersion?: string; /** True for block directives that occupy their own paragraph. */
|
|
13
13
|
block: boolean;
|
|
14
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* Nesting depth (0-based) of every block-directive opener, derived purely from
|
|
17
|
+
* the scanned ranges by containment: walk the block openers/closers in document
|
|
18
|
+
* order with a kind-aware stack, and record each opener's depth as the stack size
|
|
19
|
+
* before it is pushed. Only `block:true` if/each pairs participate (inline markers
|
|
20
|
+
* resolve within a paragraph and get no rail).
|
|
21
|
+
*
|
|
22
|
+
* Matching is kind-aware so a mid-edit / unbalanced template stays sane: a closer
|
|
23
|
+
* pops the nearest opener of the *same family* ({{/if}} ⇒ {{#if}}, {{/each}} ⇒
|
|
24
|
+
* {{#each}}), dropping any still-open openers nested above it; a closer with no
|
|
25
|
+
* matching opener is ignored (never decrements a foreign block's depth). A blind
|
|
26
|
+
* open/close counter would mis-count here: e.g. a stray {{/each}} between {{#if}}
|
|
27
|
+
* and a nested {{#each}} would wrongly pull the inner {{#each}} back to depth 0.
|
|
28
|
+
*
|
|
29
|
+
* Keyed by the opener's `from` PM position, which is unique per marker, so the
|
|
30
|
+
* overlay can look a band's depth up from its opener range. This is a pure
|
|
31
|
+
* function of the ranges (no layout), hence unit-testable in isolation; the
|
|
32
|
+
* overlay caps the *visual* indentation separately.
|
|
33
|
+
*/
|
|
34
|
+
declare const computeBlockDepths: (ranges: readonly DirectiveRange[]) => Map<number, number>;
|
|
15
35
|
declare const scanDirectives: (doc: Node) => DirectiveRange[];
|
|
16
36
|
declare const templateDirectivesKey: PluginKey<DocScanState<undefined, DirectiveRange>>;
|
|
17
37
|
type TemplateDirectivesPluginOptions = {
|
|
@@ -22,4 +42,4 @@ declare const createTemplateDirectivesPlugin: ({
|
|
|
22
42
|
}?: TemplateDirectivesPluginOptions) => import("prosemirror-state").Plugin<DocScanState<undefined, DirectiveRange>>;
|
|
23
43
|
declare const getTemplateDirectives: (state: EditorState) => readonly DirectiveRange[];
|
|
24
44
|
//#endregion
|
|
25
|
-
export { type DirectiveKind, DirectiveRange, TemplateDirectivesPluginOptions, createTemplateDirectivesPlugin, getTemplateDirectives, scanDirectives, templateDirectivesKey };
|
|
45
|
+
export { type DirectiveKind, DirectiveRange, TemplateDirectivesPluginOptions, computeBlockDepths, createTemplateDirectivesPlugin, getTemplateDirectives, scanDirectives, templateDirectivesKey };
|