@anweb/nuxt-aneditor 0.1.1

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 (59) hide show
  1. package/README.md +81 -0
  2. package/dist/module.d.mts +14 -0
  3. package/dist/module.d.ts +14 -0
  4. package/dist/module.json +9 -0
  5. package/dist/module.mjs +29 -0
  6. package/dist/runtime/assets/icons/blockquote.svg +1 -0
  7. package/dist/runtime/assets/icons/bold.svg +1 -0
  8. package/dist/runtime/assets/icons/code-block.svg +1 -0
  9. package/dist/runtime/assets/icons/code.svg +1 -0
  10. package/dist/runtime/assets/icons/edit.svg +1 -0
  11. package/dist/runtime/assets/icons/expand.svg +1 -0
  12. package/dist/runtime/assets/icons/heading.svg +1 -0
  13. package/dist/runtime/assets/icons/horizontal-rule.svg +1 -0
  14. package/dist/runtime/assets/icons/image.svg +1 -0
  15. package/dist/runtime/assets/icons/italic.svg +1 -0
  16. package/dist/runtime/assets/icons/link.svg +1 -0
  17. package/dist/runtime/assets/icons/ordered-list.svg +1 -0
  18. package/dist/runtime/assets/icons/redo.svg +1 -0
  19. package/dist/runtime/assets/icons/remove.svg +1 -0
  20. package/dist/runtime/assets/icons/strikethrough.svg +1 -0
  21. package/dist/runtime/assets/icons/table.svg +1 -0
  22. package/dist/runtime/assets/icons/task-list.svg +1 -0
  23. package/dist/runtime/assets/icons/undo.svg +1 -0
  24. package/dist/runtime/assets/icons/unordered-list.svg +1 -0
  25. package/dist/runtime/assets/icons/upload.svg +1 -0
  26. package/dist/runtime/assets/icons/youtube.svg +1 -0
  27. package/dist/runtime/components/AnEditor/Editor.vue +630 -0
  28. package/dist/runtime/components/AnEditor/Editor.vue.d.ts +2 -0
  29. package/dist/runtime/components/AnEditor/Prompt.vue +50 -0
  30. package/dist/runtime/components/AnEditor/Prompt.vue.d.ts +2 -0
  31. package/dist/runtime/components/AnEditor/Toolbar.vue +191 -0
  32. package/dist/runtime/components/AnEditor/Toolbar.vue.d.ts +2 -0
  33. package/dist/runtime/components/AnEditor/Viewer.vue +16 -0
  34. package/dist/runtime/components/AnEditor/Viewer.vue.d.ts +2 -0
  35. package/dist/runtime/composables/useBlocks.d.ts +15 -0
  36. package/dist/runtime/composables/useBlocks.js +258 -0
  37. package/dist/runtime/composables/useHistory.d.ts +12 -0
  38. package/dist/runtime/composables/useHistory.js +56 -0
  39. package/dist/runtime/composables/useImage.d.ts +27 -0
  40. package/dist/runtime/composables/useImage.js +81 -0
  41. package/dist/runtime/composables/useList.d.ts +10 -0
  42. package/dist/runtime/composables/useList.js +116 -0
  43. package/dist/runtime/composables/useSelection.d.ts +20 -0
  44. package/dist/runtime/composables/useSelection.js +92 -0
  45. package/dist/runtime/composables/useTable.d.ts +29 -0
  46. package/dist/runtime/composables/useTable.js +175 -0
  47. package/dist/runtime/types/global.d.ts +8 -0
  48. package/dist/runtime/types/index.d.ts +1 -0
  49. package/dist/runtime/types/index.js +1 -0
  50. package/dist/runtime/utils/index.d.ts +3 -0
  51. package/dist/runtime/utils/index.js +3 -0
  52. package/dist/runtime/utils/parseMarkdown.d.ts +1 -0
  53. package/dist/runtime/utils/parseMarkdown.js +184 -0
  54. package/dist/runtime/utils/toMarkdown.d.ts +1 -0
  55. package/dist/runtime/utils/toMarkdown.js +233 -0
  56. package/dist/runtime/utils/youtube.d.ts +1 -0
  57. package/dist/runtime/utils/youtube.js +6 -0
  58. package/dist/types.d.mts +3 -0
  59. package/package.json +50 -0
@@ -0,0 +1,233 @@
1
+ let sharedParser = null;
2
+ export const toMarkdown = (html) => {
3
+ if (!html || !html.trim()) return "";
4
+ if (!sharedParser) sharedParser = new DOMParser();
5
+ const doc = sharedParser.parseFromString(`<div>${html}</div>`, "text/html");
6
+ const root = doc.body.firstElementChild;
7
+ if (!root) return "";
8
+ return walkNodes(root).replace(/\n{3,}/g, "\n\n").trim();
9
+ };
10
+ const extractImgWidth = (el) => {
11
+ const style = el.getAttribute("style") ?? "";
12
+ const match = style.match(/width:\s*(\d+(?:px|%))/);
13
+ if (match) return match[1];
14
+ const widthAttr = el.getAttribute("width");
15
+ if (widthAttr) return widthAttr;
16
+ return null;
17
+ };
18
+ const walkListItems = (listEl, indent = 0) => {
19
+ let result = "";
20
+ const tag = listEl.tagName.toLowerCase();
21
+ let index = 1;
22
+ for (const child of listEl.children) {
23
+ if (child.tagName.toLowerCase() !== "li") continue;
24
+ const li = child;
25
+ const prefix = " ".repeat(indent);
26
+ const marker = tag === "ul" ? "- " : `${index}. `;
27
+ let liText = "";
28
+ for (const node of li.childNodes) {
29
+ if (node.nodeType === Node.TEXT_NODE) {
30
+ liText += node.textContent ?? "";
31
+ continue;
32
+ }
33
+ if (node.nodeType === Node.ELEMENT_NODE) {
34
+ const el = node;
35
+ const t = el.tagName.toLowerCase();
36
+ if (t === "ul" || t === "ol") continue;
37
+ liText += walkNodes(el);
38
+ continue;
39
+ }
40
+ }
41
+ result += `${prefix}${marker}${liText.trim()}
42
+ `;
43
+ for (const node of li.children) {
44
+ const t = node.tagName.toLowerCase();
45
+ if (t === "ul" || t === "ol") {
46
+ result += walkListItems(node, indent + 2);
47
+ }
48
+ }
49
+ index++;
50
+ }
51
+ return result;
52
+ };
53
+ const walkTable = (tableEl) => {
54
+ let result = "";
55
+ const thead = tableEl.querySelector("thead");
56
+ const tbody = tableEl.querySelector("tbody");
57
+ const getRowCells = (row) => {
58
+ return Array.from(row.children).map((cell) => walkNodes(cell).trim());
59
+ };
60
+ const getAlign = (cell) => {
61
+ const style = cell.getAttribute("style") ?? "";
62
+ if (style.includes("center")) return ":---:";
63
+ if (style.includes("right")) return "---:";
64
+ return "---";
65
+ };
66
+ if (thead) {
67
+ const headerRow = thead.querySelector("tr");
68
+ if (headerRow) {
69
+ const headers = getRowCells(headerRow);
70
+ const aligns = Array.from(headerRow.children).map(getAlign);
71
+ result += "| " + headers.join(" | ") + " |\n";
72
+ result += "| " + aligns.join(" | ") + " |\n";
73
+ }
74
+ }
75
+ if (tbody) {
76
+ for (const row of tbody.querySelectorAll(":scope > tr")) {
77
+ const cells = getRowCells(row);
78
+ result += "| " + cells.join(" | ") + " |\n";
79
+ }
80
+ }
81
+ result += "\n";
82
+ return result;
83
+ };
84
+ const walkNodes = (parent) => {
85
+ let result = "";
86
+ for (const node of parent.childNodes) {
87
+ if (node.nodeType === Node.TEXT_NODE) {
88
+ result += node.textContent ?? "";
89
+ continue;
90
+ }
91
+ if (node.nodeType !== Node.ELEMENT_NODE) continue;
92
+ const el = node;
93
+ const tag = el.tagName.toLowerCase();
94
+ if (/^h[1-6]$/.test(tag)) {
95
+ const level = parseInt(tag[1]);
96
+ result += `${"#".repeat(level)} ${walkNodes(el).trim()}
97
+
98
+ `;
99
+ continue;
100
+ }
101
+ if (tag === "strong" || tag === "b") {
102
+ result += `**${walkNodes(el)}**`;
103
+ continue;
104
+ }
105
+ if (tag === "em" || tag === "i") {
106
+ result += `*${walkNodes(el)}*`;
107
+ continue;
108
+ }
109
+ if (tag === "s" || tag === "del" || tag === "strike") {
110
+ result += `~~${walkNodes(el)}~~`;
111
+ continue;
112
+ }
113
+ if (tag === "a") {
114
+ const href = el.getAttribute("href") ?? "";
115
+ result += `[${walkNodes(el)}](${href})`;
116
+ continue;
117
+ }
118
+ if (tag === "img") {
119
+ const src = el.getAttribute("src") ?? "";
120
+ const alt = el.getAttribute("alt") ?? "";
121
+ const width = extractImgWidth(el);
122
+ if (width) {
123
+ result += `<img src="${src}" alt="${alt}" width="${width}" />`;
124
+ } else {
125
+ result += `![${alt}](${src})`;
126
+ }
127
+ continue;
128
+ }
129
+ if (tag === "figure") {
130
+ const img = el.querySelector("img");
131
+ if (img) {
132
+ const src = img.getAttribute("src") ?? "";
133
+ const alt = img.getAttribute("alt") ?? "";
134
+ const width = extractImgWidth(img);
135
+ if (width) {
136
+ result += `<img src="${src}" alt="${alt}" width="${width}" />
137
+
138
+ `;
139
+ } else {
140
+ result += `![${alt}](${src})
141
+
142
+ `;
143
+ }
144
+ continue;
145
+ }
146
+ }
147
+ if (tag === "pre") {
148
+ const code = el.querySelector("code");
149
+ const content = code?.textContent ?? el.textContent ?? "";
150
+ const langMatch = code?.className.match(/language-(\w+)/);
151
+ const lang = langMatch ? langMatch[1] : "";
152
+ result += `\`\`\`${lang}
153
+ ${content}
154
+ \`\`\`
155
+
156
+ `;
157
+ continue;
158
+ }
159
+ if (tag === "code") {
160
+ result += `\`${el.textContent ?? ""}\``;
161
+ continue;
162
+ }
163
+ if (tag === "hr") {
164
+ result += "---\n\n";
165
+ continue;
166
+ }
167
+ if (tag === "br") {
168
+ result += "\n";
169
+ continue;
170
+ }
171
+ if (tag === "ul" || tag === "ol") {
172
+ result += walkListItems(el, 0);
173
+ result += "\n";
174
+ continue;
175
+ }
176
+ if (tag === "table") {
177
+ result += walkTable(el);
178
+ continue;
179
+ }
180
+ if (tag === "blockquote") {
181
+ const inner = walkNodes(el).trim();
182
+ const lines = inner.split("\n");
183
+ result += lines.map((l) => `> ${l}`).join("\n") + "\n\n";
184
+ continue;
185
+ }
186
+ if (tag === "iframe") {
187
+ const src = el.getAttribute("src") ?? "";
188
+ const ytMatch = src.match(/youtube(?:-nocookie)?\.com\/embed\/([A-Za-z0-9_-]{11})/);
189
+ if (ytMatch) {
190
+ result += `https://www.youtube.com/watch?v=${ytMatch[1]}
191
+
192
+ `;
193
+ continue;
194
+ }
195
+ }
196
+ if (tag === "div" && el.classList.contains("an-editor__youtube")) {
197
+ const iframe = el.querySelector("iframe");
198
+ if (iframe) {
199
+ const src = iframe.getAttribute("src") ?? "";
200
+ const ytMatch = src.match(/youtube(?:-nocookie)?\.com\/embed\/([A-Za-z0-9_-]{11})/);
201
+ if (ytMatch) {
202
+ result += `https://www.youtube.com/watch?v=${ytMatch[1]}
203
+
204
+ `;
205
+ continue;
206
+ }
207
+ }
208
+ }
209
+ if (tag === "p") {
210
+ const inner = walkNodes(el).trim();
211
+ if (inner) {
212
+ result += `${inner}
213
+
214
+ `;
215
+ }
216
+ continue;
217
+ }
218
+ if (tag === "input") {
219
+ continue;
220
+ }
221
+ if (tag === "div" || tag === "section" || tag === "article") {
222
+ const inner = walkNodes(el).trim();
223
+ if (inner) {
224
+ result += `${inner}
225
+
226
+ `;
227
+ }
228
+ continue;
229
+ }
230
+ result += walkNodes(el);
231
+ }
232
+ return result;
233
+ };
@@ -0,0 +1 @@
1
+ export declare const extractYouTubeId: (url: string) => string | null;
@@ -0,0 +1,6 @@
1
+ export const extractYouTubeId = (url) => {
2
+ const match = url.match(
3
+ /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([A-Za-z0-9_-]{11})/
4
+ );
5
+ return match ? match[1] : null;
6
+ };
@@ -0,0 +1,3 @@
1
+ export { default } from './module.mjs'
2
+
3
+ export { type ModuleOptions } from './module.mjs'
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@anweb/nuxt-aneditor",
3
+ "version": "0.1.1",
4
+ "description": "AnEditor Nuxt module — WYSIWYG markdown editor",
5
+ "repository": "https://github.com/ANLTD/aneditor",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/types.d.mts",
11
+ "import": "./dist/module.mjs"
12
+ }
13
+ },
14
+ "main": "./dist/module.mjs",
15
+ "typesVersions": {
16
+ "*": {
17
+ ".": [
18
+ "./dist/types.d.mts"
19
+ ]
20
+ }
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "scripts": {
26
+ "prepack": "nuxt-module-build build",
27
+ "dev": "npm run dev:prepare && nuxi dev playground",
28
+ "dev:build": "nuxi build playground",
29
+ "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
30
+ "release:patch": "npm run prepack && changelogen --patch --release && npm login && npm publish --access public && git push --follow-tags",
31
+ "release:minor": "npm run prepack && changelogen --minor --release && npm login && npm publish --access public && git push --follow-tags",
32
+ "release:major": "npm run prepack && changelogen --major --release && npm login && npm publish --access public && git push --follow-tags",
33
+ "update": "npx -y npm-check-updates -u"
34
+ },
35
+ "dependencies": {
36
+ "@anweb/nuxt-ancore": "^1.16.2",
37
+ "@nuxt/icon": "^2.2.1",
38
+ "@vueuse/core": "^14.2.1"
39
+ },
40
+ "devDependencies": {
41
+ "@nuxt/devtools": "^3.2.1",
42
+ "@nuxt/kit": "^4.3.1",
43
+ "@nuxt/module-builder": "^1.0.2",
44
+ "@nuxt/schema": "^4.3.1",
45
+ "@types/node": "latest",
46
+ "changelogen": "^0.6.2",
47
+ "nuxt": "^4.3.1",
48
+ "typescript": "~5.9.3"
49
+ }
50
+ }