vuewrite 0.0.27 → 0.0.29
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/markdown.js +95 -97
- package/package.json +3 -3
package/dist/markdown.js
CHANGED
|
@@ -1,148 +1,139 @@
|
|
|
1
1
|
let _counter = 0;
|
|
2
2
|
const uid = () => (++_counter).toString();
|
|
3
|
-
function
|
|
4
|
-
const
|
|
5
|
-
return
|
|
3
|
+
function markdownToBlocks(markdown, previousBlocks = [], options = {}) {
|
|
4
|
+
const raw = parseBlocks(markdown.split("\n"), options.softBreaks ?? false);
|
|
5
|
+
return reconcileIds(raw, previousBlocks);
|
|
6
6
|
}
|
|
7
|
-
function
|
|
8
|
-
const m = oldKeys.length;
|
|
9
|
-
const n = newKeys.length;
|
|
10
|
-
const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
|
|
11
|
-
for (let i2 = 1; i2 <= m; i2++) {
|
|
12
|
-
for (let j2 = 1; j2 <= n; j2++) {
|
|
13
|
-
dp[i2][j2] = oldKeys[i2 - 1] === newKeys[j2 - 1] ? dp[i2 - 1][j2 - 1] + 1 : Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
const pairs = [];
|
|
17
|
-
let i = m;
|
|
18
|
-
let j = n;
|
|
19
|
-
while (i > 0 && j > 0) {
|
|
20
|
-
if (oldKeys[i - 1] === newKeys[j - 1]) {
|
|
21
|
-
pairs.unshift([i - 1, j - 1]);
|
|
22
|
-
i--;
|
|
23
|
-
j--;
|
|
24
|
-
} else if (dp[i - 1][j] >= dp[i][j - 1]) {
|
|
25
|
-
i--;
|
|
26
|
-
} else {
|
|
27
|
-
j--;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return pairs;
|
|
31
|
-
}
|
|
32
|
-
function markdownToBlocks(markdown, previousBlocks = []) {
|
|
33
|
-
const lines = markdown.split("\n");
|
|
7
|
+
function parseBlocks(lines, softBreaks) {
|
|
34
8
|
const blocks = [];
|
|
9
|
+
const push = (block) => blocks.push({ id: "", ...block });
|
|
35
10
|
let i = 0;
|
|
11
|
+
let prevWasBlank = false;
|
|
36
12
|
while (i < lines.length) {
|
|
37
13
|
const line = lines[i];
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
14
|
+
const wasBlank = prevWasBlank;
|
|
15
|
+
prevWasBlank = line === "";
|
|
16
|
+
const fenceMatch = line.match(/^:::\s*(\S+)/);
|
|
17
|
+
if (fenceMatch) {
|
|
18
|
+
const body = [];
|
|
42
19
|
i++;
|
|
43
|
-
while (i < lines.length && lines[i].trim() !== ":::")
|
|
44
|
-
|
|
45
|
-
i++;
|
|
46
|
-
}
|
|
47
|
-
const { text: text2, styles: styles2 } = parseInline(bodyLines.join("\n"));
|
|
48
|
-
const block2 = { id: uid(), text: text2, type };
|
|
49
|
-
if (styles2.length > 0) block2.styles = styles2;
|
|
50
|
-
blocks.push(block2);
|
|
20
|
+
while (i < lines.length && lines[i].trim() !== ":::") body.push(lines[i++]);
|
|
21
|
+
push({ type: fenceMatch[1], ...withStyles(parseInline(body.join("\n"))) });
|
|
51
22
|
i++;
|
|
52
23
|
continue;
|
|
53
24
|
}
|
|
54
|
-
const
|
|
55
|
-
if (
|
|
56
|
-
|
|
57
|
-
const { text: text2, styles: styles2 } = parseInline(xmlPairedMatch[3]);
|
|
58
|
-
const block2 = { id: uid(), text: text2, type: xmlPairedMatch[1], ...attrs };
|
|
59
|
-
if (styles2.length > 0) block2.styles = styles2;
|
|
60
|
-
blocks.push(block2);
|
|
25
|
+
const xmlPaired = line.match(/^<(\w[\w-]*)(\s[^>]*)?>(.+)<\/\1>$/);
|
|
26
|
+
if (xmlPaired) {
|
|
27
|
+
push({ type: xmlPaired[1], ...parseAttributes(xmlPaired[2] ?? ""), ...withStyles(parseInline(xmlPaired[3])) });
|
|
61
28
|
i++;
|
|
62
29
|
continue;
|
|
63
30
|
}
|
|
64
|
-
const
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
const block2 = { id: uid(), text: "", type: xmlSelfMatch[1], editable: false, ...attrs };
|
|
68
|
-
blocks.push(block2);
|
|
31
|
+
const xmlSelf = line.match(/^<(\w[\w-]*)(\s[^>]*)?\s*\/?>$/);
|
|
32
|
+
if (xmlSelf) {
|
|
33
|
+
push({ text: "", type: xmlSelf[1], editable: false, ...parseAttributes(xmlSelf[2] ?? "") });
|
|
69
34
|
i++;
|
|
70
35
|
continue;
|
|
71
36
|
}
|
|
72
37
|
if (line.startsWith("```")) {
|
|
73
38
|
const codeLines = [];
|
|
74
39
|
i++;
|
|
75
|
-
while (i < lines.length && !lines[i].startsWith("```"))
|
|
76
|
-
|
|
77
|
-
i++;
|
|
78
|
-
}
|
|
79
|
-
blocks.push({ id: uid(), text: codeLines.join("\n"), type: "code", editable: false });
|
|
40
|
+
while (i < lines.length && !lines[i].startsWith("```")) codeLines.push(lines[i++]);
|
|
41
|
+
push({ text: codeLines.join("\n"), type: "code", editable: false });
|
|
80
42
|
i++;
|
|
81
43
|
continue;
|
|
82
44
|
}
|
|
83
45
|
const headingMatch = line.match(/^(#{1,3}) (.+)$/);
|
|
84
46
|
if (headingMatch) {
|
|
85
|
-
|
|
86
|
-
const type = level === 1 ? "h1" : level === 2 ? "h2" : "h3";
|
|
87
|
-
const { text: text2, styles: styles2 } = parseInline(headingMatch[2]);
|
|
88
|
-
const block2 = { id: uid(), text: text2, type };
|
|
89
|
-
if (styles2.length > 0) block2.styles = styles2;
|
|
90
|
-
blocks.push(block2);
|
|
47
|
+
push({ type: `h${headingMatch[1].length}`, ...withStyles(parseInline(headingMatch[2])) });
|
|
91
48
|
i++;
|
|
92
49
|
continue;
|
|
93
50
|
}
|
|
94
51
|
const ulMatch = line.match(/^[-*] (.+)$/);
|
|
95
52
|
if (ulMatch) {
|
|
96
|
-
|
|
97
|
-
const block2 = { id: uid(), text: text2, type: "li" };
|
|
98
|
-
if (styles2.length > 0) block2.styles = styles2;
|
|
99
|
-
blocks.push(block2);
|
|
53
|
+
push({ type: "li", ...withStyles(parseInline(ulMatch[1])) });
|
|
100
54
|
i++;
|
|
101
55
|
continue;
|
|
102
56
|
}
|
|
103
57
|
const olMatch = line.match(/^\d+\. (.+)$/);
|
|
104
58
|
if (olMatch) {
|
|
105
|
-
|
|
106
|
-
const block2 = { id: uid(), text: text2, type: "ol" };
|
|
107
|
-
if (styles2.length > 0) block2.styles = styles2;
|
|
108
|
-
blocks.push(block2);
|
|
59
|
+
push({ type: "ol", ...withStyles(parseInline(olMatch[1])) });
|
|
109
60
|
i++;
|
|
110
61
|
continue;
|
|
111
62
|
}
|
|
112
63
|
if (line === "") {
|
|
113
|
-
if (blocks.length > 0 && i < lines.length - 1) {
|
|
114
|
-
blocks.push({ id: uid(), text: "" });
|
|
115
|
-
}
|
|
64
|
+
if (!softBreaks && blocks.length > 0 && i < lines.length - 1) push({ text: "" });
|
|
116
65
|
i++;
|
|
117
66
|
continue;
|
|
118
67
|
}
|
|
119
|
-
const
|
|
120
|
-
const
|
|
121
|
-
if (
|
|
122
|
-
|
|
68
|
+
const parsed = parseInline(line);
|
|
69
|
+
const last = blocks[blocks.length - 1];
|
|
70
|
+
if (softBreaks && !wasBlank && (last == null ? void 0 : last.type) === void 0 && (last == null ? void 0 : last.text)) {
|
|
71
|
+
appendToLastParagraph(last, parsed);
|
|
72
|
+
} else {
|
|
73
|
+
push(withStyles(parsed));
|
|
74
|
+
}
|
|
123
75
|
i++;
|
|
124
76
|
}
|
|
125
|
-
if (blocks.length === 0) {
|
|
126
|
-
|
|
77
|
+
if (blocks.length === 0) push({ text: "" });
|
|
78
|
+
return blocks;
|
|
79
|
+
}
|
|
80
|
+
function withStyles({ text, styles }) {
|
|
81
|
+
return styles.length > 0 ? { text, styles } : { text };
|
|
82
|
+
}
|
|
83
|
+
function appendToLastParagraph(block, { text, styles }) {
|
|
84
|
+
const offset = block.text.length + 1;
|
|
85
|
+
block.text += "\n" + text;
|
|
86
|
+
if (styles.length > 0) {
|
|
87
|
+
if (!block.styles) block.styles = [];
|
|
88
|
+
for (const s of styles) block.styles.push({ ...s, start: s.start + offset, end: s.end + offset });
|
|
127
89
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
90
|
+
}
|
|
91
|
+
function reconcileIds(blocks, previousBlocks) {
|
|
92
|
+
if (previousBlocks.length === 0) return blocks.map((b) => ({ ...b, id: uid() }));
|
|
93
|
+
const idMap = buildIdMap(blocks, previousBlocks);
|
|
94
|
+
return blocks.map((b, j) => ({ ...b, id: idMap.get(j) ?? uid() }));
|
|
95
|
+
}
|
|
96
|
+
function buildIdMap(blocks, previousBlocks) {
|
|
97
|
+
const oldKeys = previousBlocks.map(contentKey);
|
|
98
|
+
const newKeys = blocks.map(contentKey);
|
|
131
99
|
const pairs = lcsIndices(oldKeys, newKeys);
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
100
|
+
const map = new Map(pairs.map(([i, j]) => [j, previousBlocks[i].id]));
|
|
101
|
+
const lastJ = blocks.length - 1;
|
|
102
|
+
const lastOld = previousBlocks[previousBlocks.length - 1];
|
|
103
|
+
const lastNew = blocks[lastJ];
|
|
104
|
+
if (lastNew && lastOld && !map.has(lastJ) && lastNew.type === lastOld.type) {
|
|
105
|
+
const usedIds = new Set(map.values());
|
|
106
|
+
if (!usedIds.has(lastOld.id)) map.set(lastJ, lastOld.id);
|
|
107
|
+
}
|
|
108
|
+
return map;
|
|
137
109
|
}
|
|
138
|
-
function
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
110
|
+
function contentKey({ id: _id, ...rest }) {
|
|
111
|
+
return JSON.stringify(rest);
|
|
112
|
+
}
|
|
113
|
+
function lcsIndices(oldKeys, newKeys) {
|
|
114
|
+
const m = oldKeys.length;
|
|
115
|
+
const n = newKeys.length;
|
|
116
|
+
const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
|
|
117
|
+
for (let i2 = 1; i2 <= m; i2++) {
|
|
118
|
+
for (let j2 = 1; j2 <= n; j2++) {
|
|
119
|
+
dp[i2][j2] = oldKeys[i2 - 1] === newKeys[j2 - 1] ? dp[i2 - 1][j2 - 1] + 1 : Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
|
|
120
|
+
}
|
|
144
121
|
}
|
|
145
|
-
|
|
122
|
+
const pairs = [];
|
|
123
|
+
let i = m;
|
|
124
|
+
let j = n;
|
|
125
|
+
while (i > 0 && j > 0) {
|
|
126
|
+
if (oldKeys[i - 1] === newKeys[j - 1]) {
|
|
127
|
+
pairs.unshift([i - 1, j - 1]);
|
|
128
|
+
i--;
|
|
129
|
+
j--;
|
|
130
|
+
} else if (dp[i - 1][j] >= dp[i][j - 1]) {
|
|
131
|
+
i--;
|
|
132
|
+
} else {
|
|
133
|
+
j--;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return pairs;
|
|
146
137
|
}
|
|
147
138
|
function parseInline(md) {
|
|
148
139
|
let plainText = "";
|
|
@@ -216,6 +207,13 @@ function parseInline(md) {
|
|
|
216
207
|
}
|
|
217
208
|
return { text: plainText, styles };
|
|
218
209
|
}
|
|
210
|
+
function parseAttributes(attrStr) {
|
|
211
|
+
const result = {};
|
|
212
|
+
const re = /(\w[\w-]*)(?:=(?:"([^"]*)"|'([^']*)'|(\S+)))?/g;
|
|
213
|
+
let m;
|
|
214
|
+
while ((m = re.exec(attrStr)) !== null) result[m[1]] = m[2] ?? m[3] ?? m[4] ?? true;
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
219
217
|
const MARKERS = {
|
|
220
218
|
bold: { open: "**", close: "**" },
|
|
221
219
|
italic: { open: "*", close: "*" },
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "vuewrite",
|
|
3
3
|
"description": "Rich Text Editor based on Vue3 reactivity",
|
|
4
4
|
"private": false,
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.29",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"author": "den59k",
|
|
@@ -27,8 +27,7 @@
|
|
|
27
27
|
"dev": "vite build --watch"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"vue": "^3"
|
|
31
|
-
"vuewrite-markdown": "0.0.1"
|
|
30
|
+
"vue": "^3"
|
|
32
31
|
},
|
|
33
32
|
"devDependencies": {
|
|
34
33
|
"@vitejs/plugin-vue": "^5.0.4",
|
|
@@ -36,6 +35,7 @@
|
|
|
36
35
|
"vite": "^5.2.0",
|
|
37
36
|
"vite-plugin-dts": "^3.9.1",
|
|
38
37
|
"vuesix": "^1.0.12",
|
|
38
|
+
"vuewrite-markdown": "0.0.1",
|
|
39
39
|
"vue-tsc": "^2.0.6"
|
|
40
40
|
},
|
|
41
41
|
"files": ["dist"]
|