md2ui 1.0.16 → 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.
- package/README.md +3 -55
- package/bin/build.js +82 -7
- package/bin/md2ui.js +80 -4
- package/package.json +23 -9
- package/public/docs/00-/345/277/253/351/200/237/345/274/200/345/247/213.md +48 -28
- package/public/docs/01-/345/212/237/350/203/275/347/211/271/346/200/247.md +55 -40
- 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
- package/public/docs/02-Markdown/346/270/262/346/237/223/01-/344/273/243/347/240/201/345/235/227.md +91 -0
- package/public/docs/02-Markdown/346/270/262/346/237/223/02-/350/241/250/346/240/274.md +187 -0
- package/public/docs/02-Markdown/346/270/262/346/237/223/03-Mermaid/345/233/276/350/241/250.md +101 -0
- package/public/docs/02-Markdown/346/270/262/346/237/223/04-Frontmatter.md +32 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/public/docs/06-/351/230/205/350/257/273/344/275/223/351/252/214/assets/img-1777261394722.png +0 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/src/App.vue +130 -6
- package/src/components/AppSidebar.vue +181 -21
- package/src/components/CodeBlockNodeView.vue +72 -0
- package/src/components/DocContent.vue +25 -14
- package/src/components/EditorContent.vue +257 -0
- package/src/components/EditorToolbar.vue +264 -0
- package/src/components/ImageZoom.vue +199 -2
- package/src/components/MathBlockNodeView.vue +160 -0
- package/src/components/MathInlineNodeView.vue +145 -0
- package/src/components/MermaidNodeView.vue +149 -0
- package/src/components/TableBubbleMenu.vue +177 -0
- package/src/components/TableOfContents.vue +138 -32
- package/src/components/TopBar.vue +69 -4
- package/src/components/TreeNode.vue +232 -39
- package/src/components/WelcomePage.vue +2 -2
- package/src/composables/useDocHash.js +9 -1
- package/src/composables/useDocManager.js +325 -68
- package/src/composables/useDocTree.js +56 -1
- package/src/composables/useExportPdf.js +102 -0
- package/src/composables/useExportWord.js +73 -10
- package/src/composables/useFileWatcher.js +45 -0
- package/src/composables/useFrontmatter.js +2 -2
- package/src/composables/useMarkdown.js +529 -42
- package/src/composables/useScroll.js +47 -5
- package/src/config.js +1 -1
- package/src/extensions/CodeBlockCustom.js +113 -0
- package/src/extensions/MathBlock.js +107 -0
- package/src/extensions/MathInline.js +100 -0
- package/src/extensions/MermaidBlock.js +73 -0
- package/src/extensions/TableControls.js +670 -0
- package/src/services/DocService.js +184 -0
- package/src/style.css +2194 -39
- package/vite-plugin-doc-api.js +368 -0
- package/vite.config.js +2 -1
- package/public/docs/02-Mermaid/345/233/276/350/241/250.md +0 -102
- 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
- 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
- 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
- package/public/docs/04-API/345/217/202/350/200/203/01-/347/273/204/344/273/266API.md +0 -80
- package/public/docs/04-API/345/217/202/350/200/203/02-Composables.md +0 -92
- 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
|
+
}
|