@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.
Files changed (35) hide show
  1. package/esm/ExtensionOdt.d.ts.map +1 -1
  2. package/esm/ExtensionOdt.js +3 -1
  3. package/esm/ExtensionOdt.js.map +1 -0
  4. package/esm/OdtParser.js +1 -0
  5. package/esm/OdtParser.js.map +1 -0
  6. package/esm/lists.js +1 -0
  7. package/esm/lists.js.map +1 -0
  8. package/esm/node_handlers/basic_node_handlers.js +1 -0
  9. package/esm/node_handlers/basic_node_handlers.js.map +1 -0
  10. package/esm/node_handlers/list_node_handlers.js +1 -0
  11. package/esm/node_handlers/list_node_handlers.js.map +1 -0
  12. package/esm/node_handlers/table_node_handlers.js +1 -0
  13. package/esm/node_handlers/table_node_handlers.js.map +1 -0
  14. package/esm/postprocess/convertCodeParagraphsToCodeBlocks.js +1 -0
  15. package/esm/postprocess/convertCodeParagraphsToCodeBlocks.js.map +1 -0
  16. package/esm/postprocess/convertMathMl.js +1 -0
  17. package/esm/postprocess/convertMathMl.js.map +1 -0
  18. package/esm/postprocess/fixContinuedLists.js +1 -0
  19. package/esm/postprocess/fixContinuedLists.js.map +1 -0
  20. package/esm/postprocess/postProcess.js +1 -0
  21. package/esm/postprocess/postProcess.js.map +1 -0
  22. package/esm/postprocess/removeUnusedBookmarks.js +1 -0
  23. package/esm/postprocess/removeUnusedBookmarks.js.map +1 -0
  24. package/package.json +7 -3
  25. package/src/ExtensionOdt.ts +203 -0
  26. package/src/OdtParser.ts +350 -0
  27. package/src/lists.ts +35 -0
  28. package/src/node_handlers/basic_node_handlers.ts +155 -0
  29. package/src/node_handlers/list_node_handlers.ts +131 -0
  30. package/src/node_handlers/table_node_handlers.ts +28 -0
  31. package/src/postprocess/convertCodeParagraphsToCodeBlocks.ts +118 -0
  32. package/src/postprocess/convertMathMl.ts +56 -0
  33. package/src/postprocess/fixContinuedLists.ts +113 -0
  34. package/src/postprocess/postProcess.ts +23 -0
  35. 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
+ };