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.
Files changed (2) hide show
  1. package/dist/markdown.js +95 -97
  2. 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 blockContentKey(block) {
4
- const { id: _id, ...rest } = block;
5
- return JSON.stringify(rest);
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 lcsIndices(oldKeys, newKeys) {
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 customFenceMatch = line.match(/^:::\s*(\S+)/);
39
- if (customFenceMatch) {
40
- const type = customFenceMatch[1];
41
- const bodyLines = [];
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
- bodyLines.push(lines[i]);
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 xmlPairedMatch = line.match(/^<(\w[\w-]*)(\s[^>]*)?>(.+)<\/\1>$/);
55
- if (xmlPairedMatch) {
56
- const attrs = parseAttributes(xmlPairedMatch[2] ?? "");
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 xmlSelfMatch = line.match(/^<(\w[\w-]*)(\s[^>]*)?\s*\/?>$/);
65
- if (xmlSelfMatch) {
66
- const attrs = parseAttributes(xmlSelfMatch[2] ?? "");
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
- codeLines.push(lines[i]);
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
- const level = headingMatch[1].length;
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
- const { text: text2, styles: styles2 } = parseInline(ulMatch[1]);
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
- const { text: text2, styles: styles2 } = parseInline(olMatch[1]);
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 { text, styles } = parseInline(line);
120
- const block = { id: uid(), text };
121
- if (styles.length > 0) block.styles = styles;
122
- blocks.push(block);
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
- blocks.push({ id: uid(), text: "" });
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
- if (previousBlocks.length === 0) return blocks;
129
- const oldKeys = previousBlocks.map(blockContentKey);
130
- const newKeys = blocks.map(blockContentKey);
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 oldIdByNewIndex = new Map(pairs.map(([i2, j]) => [j, previousBlocks[i2].id]));
133
- return blocks.map((block, j) => {
134
- const oldId = oldIdByNewIndex.get(j);
135
- return oldId !== void 0 ? { ...block, id: oldId } : block;
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 parseAttributes(attrStr) {
139
- const result = {};
140
- const re = /(\w[\w-]*)(?:=(?:"([^"]*)"|'([^']*)'|(\S+)))?/g;
141
- let m;
142
- while ((m = re.exec(attrStr)) !== null) {
143
- result[m[1]] = m[2] ?? m[3] ?? m[4] ?? true;
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
- return result;
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.27",
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"]