@kerebron/extension-odt 0.4.28 → 0.4.30
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/esm/ExtensionOdt.d.ts.map +1 -1
- package/esm/ExtensionOdt.js +3 -1
- package/esm/ExtensionOdt.js.map +1 -0
- package/esm/OdtParser.js +1 -0
- package/esm/OdtParser.js.map +1 -0
- package/esm/lists.js +1 -0
- package/esm/lists.js.map +1 -0
- package/esm/node_handlers/basic_node_handlers.js +1 -0
- package/esm/node_handlers/basic_node_handlers.js.map +1 -0
- package/esm/node_handlers/list_node_handlers.js +1 -0
- package/esm/node_handlers/list_node_handlers.js.map +1 -0
- package/esm/node_handlers/table_node_handlers.js +1 -0
- package/esm/node_handlers/table_node_handlers.js.map +1 -0
- package/esm/postprocess/convertCodeParagraphsToCodeBlocks.js +1 -0
- package/esm/postprocess/convertCodeParagraphsToCodeBlocks.js.map +1 -0
- package/esm/postprocess/convertMathMl.js +1 -0
- package/esm/postprocess/convertMathMl.js.map +1 -0
- package/esm/postprocess/fixContinuedLists.js +1 -0
- package/esm/postprocess/fixContinuedLists.js.map +1 -0
- package/esm/postprocess/postProcess.js +1 -0
- package/esm/postprocess/postProcess.js.map +1 -0
- package/esm/postprocess/removeUnusedBookmarks.js +1 -0
- package/esm/postprocess/removeUnusedBookmarks.js.map +1 -0
- package/package.json +7 -3
- package/src/ExtensionOdt.ts +203 -0
- package/src/OdtParser.ts +350 -0
- package/src/lists.ts +35 -0
- package/src/node_handlers/basic_node_handlers.ts +155 -0
- package/src/node_handlers/list_node_handlers.ts +131 -0
- package/src/node_handlers/table_node_handlers.ts +28 -0
- package/src/postprocess/convertCodeParagraphsToCodeBlocks.ts +118 -0
- package/src/postprocess/convertMathMl.ts +56 -0
- package/src/postprocess/fixContinuedLists.ts +113 -0
- package/src/postprocess/postProcess.ts +23 -0
- package/src/postprocess/removeUnusedBookmarks.ts +36 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Fragment, MarkType, Node } from 'prosemirror-model';
|
|
2
|
+
import { Transaction } from 'prosemirror-state';
|
|
3
|
+
|
|
4
|
+
import type { Command } from '@kerebron/editor/commands';
|
|
5
|
+
|
|
6
|
+
function onlyHasCodeMarkedText(
|
|
7
|
+
paragraph: Node,
|
|
8
|
+
codeMarkType: MarkType,
|
|
9
|
+
): boolean {
|
|
10
|
+
if (paragraph.content.size === 0) {
|
|
11
|
+
return paragraph.marks.some((mark) => mark.type.name === codeMarkType.name);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let allAreCodeMarked = true;
|
|
15
|
+
|
|
16
|
+
paragraph.content.forEach((child) => {
|
|
17
|
+
if (
|
|
18
|
+
!child.isText ||
|
|
19
|
+
!child.marks.some((mark) => mark.type.name === codeMarkType.name)
|
|
20
|
+
) {
|
|
21
|
+
allAreCodeMarked = false;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return allAreCodeMarked;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface ParagraphsToMerge {
|
|
29
|
+
startPos: number;
|
|
30
|
+
endPos: number;
|
|
31
|
+
innerText: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const convertCodeParagraphsToCodeBlocks: Command = (
|
|
35
|
+
state,
|
|
36
|
+
dispatch,
|
|
37
|
+
): boolean => {
|
|
38
|
+
const doc: Node = state.doc;
|
|
39
|
+
const schema = state.schema;
|
|
40
|
+
let tr: Transaction = state.tr;
|
|
41
|
+
|
|
42
|
+
let paragraphsToMerge: ParagraphsToMerge | null = null;
|
|
43
|
+
|
|
44
|
+
function flushCodeBlock() {
|
|
45
|
+
if (paragraphsToMerge === null) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const textNode = schema.text(paragraphsToMerge.innerText);
|
|
50
|
+
const codeBlock = schema.nodes.code_block.createAndFill(null, [textNode]);
|
|
51
|
+
|
|
52
|
+
const startPos = tr.mapping.map(paragraphsToMerge.startPos);
|
|
53
|
+
const endPos = tr.mapping.map(paragraphsToMerge.endPos);
|
|
54
|
+
|
|
55
|
+
if (codeBlock) {
|
|
56
|
+
tr.replaceRangeWith(startPos, endPos, codeBlock);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
paragraphsToMerge = null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function nodesToText(fragment: Fragment) {
|
|
63
|
+
if (fragment.content.length === 0) {
|
|
64
|
+
return '';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let retVal = '';
|
|
68
|
+
|
|
69
|
+
fragment.content.forEach((node) => {
|
|
70
|
+
if (node.isText) {
|
|
71
|
+
retVal += node.text;
|
|
72
|
+
} else {
|
|
73
|
+
retVal = '@TODO: node.type ' + node.type;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return retVal;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
doc.forEach((node, pos) => {
|
|
81
|
+
if (node.type.name === 'paragraph') {
|
|
82
|
+
const isCodeOnly = onlyHasCodeMarkedText(node, schema.marks.code);
|
|
83
|
+
const isEmpty = node.content.size === 0;
|
|
84
|
+
|
|
85
|
+
if (isCodeOnly) {
|
|
86
|
+
if (paragraphsToMerge === null) {
|
|
87
|
+
paragraphsToMerge = {
|
|
88
|
+
startPos: pos,
|
|
89
|
+
endPos: pos + node.nodeSize,
|
|
90
|
+
innerText: nodesToText(node.content),
|
|
91
|
+
};
|
|
92
|
+
} else {
|
|
93
|
+
paragraphsToMerge = {
|
|
94
|
+
startPos: paragraphsToMerge.startPos,
|
|
95
|
+
endPos: pos + node.nodeSize,
|
|
96
|
+
innerText: paragraphsToMerge.innerText + '\n' +
|
|
97
|
+
nodesToText(node.content),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (paragraphsToMerge !== null) {
|
|
105
|
+
flushCodeBlock();
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (paragraphsToMerge !== null) {
|
|
110
|
+
flushCodeBlock();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (dispatch) {
|
|
114
|
+
dispatch(tr);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return tr.steps.length > 0;
|
|
118
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { off } from 'node:process';
|
|
2
|
+
import { Node } from 'prosemirror-model';
|
|
3
|
+
import { Command } from 'prosemirror-state';
|
|
4
|
+
|
|
5
|
+
// Related tests:
|
|
6
|
+
// test ./example-document.md
|
|
7
|
+
export const convertMathMl: Command = (state, dispatch): boolean => {
|
|
8
|
+
const doc: Node = state.doc;
|
|
9
|
+
const schema = state.schema;
|
|
10
|
+
const mathType = schema.nodes.math;
|
|
11
|
+
const codeBlockType = schema.nodes.code_block;
|
|
12
|
+
|
|
13
|
+
if (!mathType || !codeBlockType) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let modified = false;
|
|
18
|
+
|
|
19
|
+
const tr = state.tr;
|
|
20
|
+
|
|
21
|
+
doc.forEach((parentNode, offset, index) => {
|
|
22
|
+
if (parentNode.childCount !== 1) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const parentPos = offset;
|
|
27
|
+
|
|
28
|
+
const node = parentNode.child(0);
|
|
29
|
+
|
|
30
|
+
if (node.type !== mathType) return;
|
|
31
|
+
if (node.attrs.content.length === 0) return;
|
|
32
|
+
|
|
33
|
+
const codeBlock = codeBlockType.create(
|
|
34
|
+
{
|
|
35
|
+
lang: node.attrs.lang,
|
|
36
|
+
},
|
|
37
|
+
schema.text(node.attrs.content),
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
tr.replaceWith(
|
|
41
|
+
tr.mapping.map(parentPos),
|
|
42
|
+
tr.mapping.map(parentPos + parentNode.nodeSize),
|
|
43
|
+
codeBlock,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
modified = true;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (!modified) return false;
|
|
50
|
+
|
|
51
|
+
if (dispatch) {
|
|
52
|
+
dispatch(tr);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return true;
|
|
56
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Fragment, Node, Slice } from 'prosemirror-model';
|
|
2
|
+
import { Command } from 'prosemirror-state';
|
|
3
|
+
|
|
4
|
+
import { nodeToTreeString } from '@kerebron/editor';
|
|
5
|
+
|
|
6
|
+
const ANY_LIST = ['ordered_list', 'bullet_list'];
|
|
7
|
+
|
|
8
|
+
interface ListPos {
|
|
9
|
+
start: number;
|
|
10
|
+
end: number;
|
|
11
|
+
stitchingDepth: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getListEndingMaxDepth(list: Node | null): number {
|
|
15
|
+
if (!list) {
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (ANY_LIST.indexOf(list.type.name) === -1) {
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (list.lastChild?.type.name === 'list_item') {
|
|
24
|
+
return 1 + getListEndingMaxDepth(list.lastChild?.lastChild);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function convertToStitchingLevels(list: Node, array: Array<Slice> = []) {
|
|
31
|
+
if (ANY_LIST.indexOf(list.type.name) === -1) {
|
|
32
|
+
throw new Error(`Incorrect list type: ${list.type.name}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!list?.firstChild) {
|
|
36
|
+
return array;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const firstChildParagraph = list.firstChild.firstChild;
|
|
40
|
+
|
|
41
|
+
if (firstChildParagraph?.content.size === 0) {
|
|
42
|
+
if (list?.firstChild.children.length < 2) {
|
|
43
|
+
array.push(new Slice(Fragment.from([]), 0, 0));
|
|
44
|
+
// array.push(list.slice(list.firstChild.nodeSize, list.firstChild.nodeSize)) // empty slice
|
|
45
|
+
return array;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const subList = list?.firstChild.child(1);
|
|
49
|
+
|
|
50
|
+
array.push(list.slice(list.firstChild.nodeSize, list.content.size));
|
|
51
|
+
|
|
52
|
+
if (ANY_LIST.indexOf(subList?.type.name) > -1) {
|
|
53
|
+
convertToStitchingLevels(subList, array);
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
array.push(list.slice(0, list.content.size));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return array;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const fixContinuedLists: Command = (state, dispatch): boolean => {
|
|
63
|
+
const doc: Node = state.doc;
|
|
64
|
+
let tr = state.tr;
|
|
65
|
+
|
|
66
|
+
let previousList: ListPos | null = null;
|
|
67
|
+
|
|
68
|
+
// console.debug(nodeToTreeString(doc));
|
|
69
|
+
|
|
70
|
+
doc.forEach((child, childPos) => {
|
|
71
|
+
if (child.type.name === 'ordered_list') {
|
|
72
|
+
const start = childPos;
|
|
73
|
+
const end = start + child.nodeSize;
|
|
74
|
+
|
|
75
|
+
if (previousList) {
|
|
76
|
+
const stitchingLevels = convertToStitchingLevels(child);
|
|
77
|
+
|
|
78
|
+
if (stitchingLevels.length <= previousList.stitchingDepth) {
|
|
79
|
+
const betweenStart = previousList.end;
|
|
80
|
+
const betweenNodes = doc.slice(betweenStart, start);
|
|
81
|
+
|
|
82
|
+
tr = tr.delete(tr.mapping.map(betweenStart), tr.mapping.map(end));
|
|
83
|
+
|
|
84
|
+
let posToInsert = tr.mapping.map(previousList.end);
|
|
85
|
+
for (const level of stitchingLevels) {
|
|
86
|
+
posToInsert -= 1; // Before /OL token
|
|
87
|
+
tr = tr.replace(posToInsert, posToInsert, level);
|
|
88
|
+
posToInsert -= 1; // Before /LI token
|
|
89
|
+
}
|
|
90
|
+
if (betweenNodes.size > 0) {
|
|
91
|
+
tr = tr.replace(posToInsert, posToInsert, betweenNodes);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
previousList.stitchingDepth = getListEndingMaxDepth(child);
|
|
95
|
+
previousList.end = end;
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
previousList = {
|
|
101
|
+
stitchingDepth: getListEndingMaxDepth(child),
|
|
102
|
+
start,
|
|
103
|
+
end,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (dispatch) {
|
|
109
|
+
dispatch(tr);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return tr.steps.length > 0;
|
|
113
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Node } from 'prosemirror-model';
|
|
2
|
+
import { Command } from 'prosemirror-state';
|
|
3
|
+
|
|
4
|
+
import { convertCodeParagraphsToCodeBlocks } from './convertCodeParagraphsToCodeBlocks.js';
|
|
5
|
+
import { removeUnusedBookmarks } from './removeUnusedBookmarks.js';
|
|
6
|
+
import { fixContinuedLists } from './fixContinuedLists.js';
|
|
7
|
+
import { convertMathMl } from './convertMathMl.js';
|
|
8
|
+
|
|
9
|
+
export interface PostProcessConfig {
|
|
10
|
+
doc: Node;
|
|
11
|
+
filesMap: Record<string, Uint8Array>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getDefaultsPostProcessFilters(
|
|
15
|
+
{ doc, filesMap }: PostProcessConfig,
|
|
16
|
+
): Array<Command> {
|
|
17
|
+
return [
|
|
18
|
+
convertCodeParagraphsToCodeBlocks,
|
|
19
|
+
removeUnusedBookmarks,
|
|
20
|
+
fixContinuedLists,
|
|
21
|
+
convertMathMl,
|
|
22
|
+
];
|
|
23
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Fragment, MarkType, Node, Schema } from 'prosemirror-model';
|
|
2
|
+
import { Command } from 'prosemirror-state';
|
|
3
|
+
|
|
4
|
+
export const removeUnusedBookmarks: Command = (state, dispatch): boolean => {
|
|
5
|
+
return false;
|
|
6
|
+
function condition(mark) {
|
|
7
|
+
console.log('rrrr', mark.type.name);
|
|
8
|
+
return mark.type.name === 'bookmark';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (node.marks) {
|
|
12
|
+
// For text nodes, filter out the marks that match the condition
|
|
13
|
+
const newMarks = node.marks.filter((mark) => !condition(mark));
|
|
14
|
+
|
|
15
|
+
// If marks were removed, return a new text node with the remaining marks
|
|
16
|
+
if (newMarks.length !== node.marks.length) {
|
|
17
|
+
return node.mark(newMarks);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Otherwise, return the original text node
|
|
21
|
+
return node;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// if (Array.isArray(node.content)) {
|
|
25
|
+
// const content: Node[] = node.content.content.map(childNode => removeUnusedBookmarks(childNode));
|
|
26
|
+
// return node.copy(content);
|
|
27
|
+
// } else {
|
|
28
|
+
// console.log('node.content', node.content);
|
|
29
|
+
// }
|
|
30
|
+
|
|
31
|
+
if (dispatch) {
|
|
32
|
+
dispatch(tr);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return tr.steps.length > 0;
|
|
36
|
+
};
|