md2ui 1.0.18 → 1.0.19

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 (74) hide show
  1. package/README.md +3 -55
  2. package/bin/build.js +82 -7
  3. package/bin/md2ui.js +80 -4
  4. package/package.json +23 -9
  5. package/public/docs/00-/345/277/253/351/200/237/345/274/200/345/247/213.md +48 -28
  6. package/public/docs/01-/345/212/237/350/203/275/347/211/271/346/200/247.md +55 -40
  7. package/public/docs/02-Markdown/346/270/262/346/237/223/00-/345/237/272/347/241/200/350/257/255/346/263/225.md +86 -0
  8. package/public/docs/02-Markdown/346/270/262/346/237/223/01-/344/273/243/347/240/201/345/235/227.md +91 -0
  9. package/public/docs/02-Markdown/346/270/262/346/237/223/02-/350/241/250/346/240/274.md +187 -0
  10. package/public/docs/02-Markdown/346/270/262/346/237/223/03-Mermaid/345/233/276/350/241/250.md +101 -0
  11. package/public/docs/02-Markdown/346/270/262/346/237/223/04-Frontmatter.md +32 -0
  12. package/public/docs/02-Markdown/346/270/262/346/237/223/05-/346/225/260/345/255/246/345/205/254/345/274/217.md +47 -0
  13. package/public/docs/03-/345/257/274/350/210/252/344/270/216/345/270/203/345/261/200/00-/344/270/211/346/240/217/345/270/203/345/261/200.md +33 -0
  14. package/public/docs/03-/345/257/274/350/210/252/344/270/216/345/270/203/345/261/200/01-/347/233/256/345/275/225/346/240/221/345/257/274/350/210/252.md +43 -0
  15. package/public/docs/03-/345/257/274/350/210/252/344/270/216/345/270/203/345/261/200/02-/346/226/207/346/241/243/345/244/247/347/272/262.md +51 -0
  16. package/public/docs/03-/345/257/274/350/210/252/344/270/216/345/270/203/345/261/200/03-/344/270/212/344/270/213/347/257/207/345/257/274/350/210/252.md +29 -0
  17. package/public/docs/03-/345/257/274/350/210/252/344/270/216/345/270/203/345/261/200/04-/347/253/231/345/206/205/351/223/276/346/216/245.md +39 -0
  18. package/public/docs/04-/346/220/234/347/264/242/345/212/237/350/203/275/00-/345/205/250/346/226/207/346/220/234/347/264/242.md +46 -0
  19. package/public/docs/05-/347/274/226/350/276/221/345/212/237/350/203/275/00-/347/274/226/350/276/221/345/231/250/345/237/272/347/241/200.md +65 -0
  20. package/public/docs/05-/347/274/226/350/276/221/345/212/237/350/203/275/01-/350/207/252/345/212/250/344/277/235/345/255/230.md +38 -0
  21. package/public/docs/06-/351/230/205/350/257/273/344/275/223/351/252/214/00-/351/230/205/350/257/273/350/277/233/345/272/246.md +43 -0
  22. package/public/docs/06-/351/230/205/350/257/273/344/275/223/351/252/214/01-/345/233/276/347/211/207/346/224/276/345/244/247.md +40 -0
  23. package/public/docs/06-/351/230/205/350/257/273/344/275/223/351/252/214/02-/350/277/224/345/233/236/351/241/266/351/203/250.md +38 -0
  24. package/public/docs/06-/351/230/205/350/257/273/344/275/223/351/252/214/assets/img-1777261394722.png +0 -0
  25. package/public/docs/07-/347/247/273/345/212/250/347/253/257/351/200/202/351/205/215/00-/345/223/215/345/272/224/345/274/217/345/270/203/345/261/200.md +37 -0
  26. package/public/docs/08-/346/226/207/346/241/243/347/256/241/347/220/206/00-/346/226/260/345/273/272/344/270/216/345/210/240/351/231/244.md +47 -0
  27. package/public/docs/09-/345/257/274/345/207/272/345/212/237/350/203/275/00-/345/257/274/345/207/272Word.md +77 -0
  28. package/public/docs/10-/351/203/250/347/275/262/344/270/216/351/205/215/347/275/256/00-CLI/345/267/245/345/205/267.md +52 -0
  29. package/public/docs/10-/351/203/250/347/275/262/344/270/216/351/205/215/347/275/256/01-SSG/351/235/231/346/200/201/346/236/204/345/273/272.md +44 -0
  30. package/public/docs/10-/351/203/250/347/275/262/344/270/216/351/205/215/347/275/256/02-/350/207/252/345/256/232/344/271/211/351/205/215/347/275/256.md +58 -0
  31. package/public/docs/11-/345/244/232/347/272/247/347/233/256/345/275/225/346/265/213/350/257/225/00-/344/270/200/347/272/247/346/226/207/346/241/243.md +20 -0
  32. package/public/docs/11-/345/244/232/347/272/247/347/233/256/345/275/225/346/265/213/350/257/225/01-/345/255/220/347/233/256/345/275/225/00-/344/272/214/347/272/247/346/226/207/346/241/243.md +13 -0
  33. package/public/docs/11-/345/244/232/347/272/247/347/233/256/345/275/225/346/265/213/350/257/225/01-/345/255/220/347/233/256/345/275/225/01-/346/267/261/345/261/202/345/265/214/345/245/227/00-/344/270/211/347/272/247/346/226/207/346/241/243.md +23 -0
  34. package/src/App.vue +130 -6
  35. package/src/components/AppSidebar.vue +181 -21
  36. package/src/components/CodeBlockNodeView.vue +72 -0
  37. package/src/components/DocContent.vue +25 -14
  38. package/src/components/EditorContent.vue +257 -0
  39. package/src/components/EditorToolbar.vue +264 -0
  40. package/src/components/ImageZoom.vue +199 -2
  41. package/src/components/MathBlockNodeView.vue +160 -0
  42. package/src/components/MathInlineNodeView.vue +145 -0
  43. package/src/components/MermaidNodeView.vue +149 -0
  44. package/src/components/TableBubbleMenu.vue +177 -0
  45. package/src/components/TableOfContents.vue +138 -32
  46. package/src/components/TopBar.vue +69 -4
  47. package/src/components/TreeNode.vue +232 -39
  48. package/src/components/WelcomePage.vue +2 -2
  49. package/src/composables/useDocHash.js +9 -1
  50. package/src/composables/useDocManager.js +325 -68
  51. package/src/composables/useDocTree.js +56 -1
  52. package/src/composables/useExportPdf.js +102 -0
  53. package/src/composables/useExportWord.js +73 -10
  54. package/src/composables/useFileWatcher.js +45 -0
  55. package/src/composables/useFrontmatter.js +2 -2
  56. package/src/composables/useMarkdown.js +529 -42
  57. package/src/composables/useScroll.js +47 -5
  58. package/src/config.js +1 -1
  59. package/src/extensions/CodeBlockCustom.js +113 -0
  60. package/src/extensions/MathBlock.js +107 -0
  61. package/src/extensions/MathInline.js +100 -0
  62. package/src/extensions/MermaidBlock.js +73 -0
  63. package/src/extensions/TableControls.js +670 -0
  64. package/src/services/DocService.js +184 -0
  65. package/src/style.css +2194 -39
  66. package/vite-plugin-doc-api.js +368 -0
  67. package/vite.config.js +2 -1
  68. package/public/docs/02-Mermaid/345/233/276/350/241/250.md +0 -102
  69. package/public/docs/03-/350/277/233/351/230/266/346/214/207/345/215/227/01-/347/233/256/345/275/225/347/273/223/346/236/204.md +0 -55
  70. package/public/docs/03-/350/277/233/351/230/266/346/214/207/345/215/227/02-/350/207/252/345/256/232/344/271/211/351/205/215/347/275/256.md +0 -63
  71. package/public/docs/03-/350/277/233/351/230/266/346/214/207/345/215/227/03-/351/203/250/347/275/262/346/226/271/346/241/210.md +0 -73
  72. package/public/docs/04-API/345/217/202/350/200/203/01-/347/273/204/344/273/266API.md +0 -80
  73. package/public/docs/04-API/345/217/202/350/200/203/02-Composables.md +0 -92
  74. package/src/api/docs.js +0 -106
@@ -0,0 +1,184 @@
1
+ /**
2
+ * 文档服务层(单例)
3
+ * 统一使用 /@user-docs-* 路由,dev 和 CLI 模式共用同一套 API
4
+ * 上层(useFileWatcher / useDocManager)只管调方法,不关心底层差异
5
+ */
6
+
7
+ // ===== ETag 缓存 =====
8
+ let _listEtag = null
9
+ let _contentEtag = null
10
+ let _contentDocPath = null
11
+
12
+ // 过滤掉没有任何文档(递归)的空目录
13
+ function pruneEmpty(children) {
14
+ return children.filter(item => {
15
+ if (item.type === 'file') return true
16
+ if (item.type === 'folder') {
17
+ item.children = pruneEmpty(item.children || [])
18
+ return item.children.length > 0
19
+ }
20
+ return false
21
+ })
22
+ }
23
+
24
+ // ===== 公开 API =====
25
+
26
+ /**
27
+ * 获取文档列表(首次加载用,不带 ETag)
28
+ * 返回构建好的树
29
+ */
30
+ export async function fetchDocsList() {
31
+ try {
32
+ const res = await fetch('/@user-docs-list')
33
+ if (res.ok) {
34
+ _listEtag = res.headers.get('etag')
35
+ return pruneEmpty(await res.json())
36
+ }
37
+ } catch { /* ignore */ }
38
+ return []
39
+ }
40
+
41
+ /**
42
+ * 轮询文档列表(带 ETag,无变化返回 null)
43
+ * 返回构建好的树 或 null
44
+ */
45
+ export async function pollDocsList() {
46
+ try {
47
+ const res = await fetch('/@user-docs-list', {
48
+ headers: _listEtag ? { 'If-None-Match': _listEtag } : {}
49
+ })
50
+ if (res.status === 304) return null
51
+ if (res.status === 200) {
52
+ _listEtag = res.headers.get('etag')
53
+ return pruneEmpty(await res.json())
54
+ }
55
+ } catch { /* 静默忽略 */ }
56
+ return null
57
+ }
58
+
59
+ /**
60
+ * 轮询文档内容(带 ETag,无变化返回 null)
61
+ * @param {string} docPath - 文档相对路径
62
+ * @returns {string|null} 内容文本或 null
63
+ */
64
+ export async function pollDocContent(docPath) {
65
+ if (!docPath) return null
66
+ // 文档切换时重置 ETag
67
+ if (docPath !== _contentDocPath) {
68
+ _contentEtag = null
69
+ _contentDocPath = docPath
70
+ }
71
+ const url = `/@user-docs/${docPath.split('/').map(encodeURIComponent).join('/')}`
72
+ try {
73
+ const res = await fetch(url, {
74
+ headers: _contentEtag ? { 'If-None-Match': _contentEtag } : {}
75
+ })
76
+ if (res.status === 304) return null
77
+ if (res.status === 200) {
78
+ _contentEtag = res.headers.get('etag')
79
+ // 捕获最后修改时间
80
+ const lm = res.headers.get('x-last-modified')
81
+ if (lm) _lastModified = lm
82
+ return await res.text()
83
+ }
84
+ } catch { /* 静默忽略 */ }
85
+ return null
86
+ }
87
+
88
+ /** 重置内容 ETag(保存/切换文档后调用) */
89
+ export function resetContentEtag() {
90
+ _contentEtag = null
91
+ _contentDocPath = null
92
+ }
93
+
94
+ /** 重置列表 ETag(强制下次拉取最新) */
95
+ export function resetListEtag() {
96
+ _listEtag = null
97
+ }
98
+
99
+ // ===== 文档最后修改时间 =====
100
+ let _lastModified = null
101
+
102
+ /** 获取当前文档的最后修改时间 */
103
+ export function getLastModified() {
104
+ return _lastModified
105
+ }
106
+
107
+ /** 重置最后修改时间 */
108
+ export function resetLastModified() {
109
+ _lastModified = null
110
+ }
111
+
112
+ // ===== 写操作 API =====
113
+
114
+ async function postJson(url, body) {
115
+ const res = await fetch(url, {
116
+ method: 'POST',
117
+ headers: { 'Content-Type': 'application/json' },
118
+ body: JSON.stringify(body)
119
+ })
120
+ return res
121
+ }
122
+
123
+ export async function saveDoc(filePath, content) {
124
+ const res = await postJson('/api/save', { path: filePath, content })
125
+ return res.ok
126
+ }
127
+
128
+ export async function createDoc(apiPath, type) {
129
+ const res = await postJson('/api/create', { path: apiPath, type })
130
+ if (!res.ok) {
131
+ const msg = await res.text()
132
+ throw new Error(msg)
133
+ }
134
+ }
135
+
136
+ export async function deleteDoc(apiPath) {
137
+ const res = await postJson('/api/delete', { path: apiPath })
138
+ if (!res.ok) {
139
+ const msg = await res.text()
140
+ throw new Error(msg)
141
+ }
142
+ }
143
+
144
+ export async function renameDoc(oldPath, newPath) {
145
+ const res = await postJson('/api/rename', { oldPath, newPath })
146
+ if (!res.ok) {
147
+ const msg = await res.text()
148
+ throw new Error(msg)
149
+ }
150
+ }
151
+
152
+ export async function reorderDocs(items) {
153
+ const res = await postJson('/api/reorder', { items })
154
+ if (!res.ok) {
155
+ const msg = await res.text()
156
+ throw new Error(msg)
157
+ }
158
+ }
159
+
160
+ /**
161
+ * 上传图片(粘贴/拖拽)
162
+ * @param {File|Blob} file - 图片文件
163
+ * @param {string} docPath - 当前文档路径(如 "03-Markdown渲染/01-代码块.md")
164
+ * @returns {Promise<string>} 图片 URL
165
+ */
166
+ export async function uploadImage(file, docPath) {
167
+ const res = await fetch('/@upload-image', {
168
+ method: 'POST',
169
+ headers: {
170
+ 'Content-Type': file.type || 'image/png',
171
+ 'X-Doc-Path': encodeURIComponent(docPath),
172
+ 'X-File-Name': encodeURIComponent(file.name || `img-${Date.now()}.png`),
173
+ },
174
+ body: file,
175
+ })
176
+
177
+ if (!res.ok) {
178
+ const msg = await res.text()
179
+ throw new Error(msg || '图片上传失败')
180
+ }
181
+
182
+ const data = await res.json()
183
+ return data.url
184
+ }