md2ui 1.0.19 → 1.0.21
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 +65 -20
- package/bin/build.js +13 -2
- package/bin/md2ui.js +25 -12
- package/package.json +4 -4
- 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 +2 -0
- package/public/docs/02-Markdown/346/270/262/346/237/223/06-Mermaid/345/244/215/346/235/202/345/233/276/350/241/250/346/265/213/350/257/225.md +1376 -0
- package/public/docs/02-Markdown/346/270/262/346/237/223/assets/img-1777383093712.png +0 -0
- package/public/docs/03-/345/257/274/350/210/252/344/270/216/345/270/203/345/261/200/05-/345/244/247/347/272/262/345/216/213/345/212/233/346/265/213/350/257/225.md +340 -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 +4 -4
- package/src/App.vue +36 -61
- package/src/components/ImageZoom.vue +9 -123
- package/src/components/MermaidNodeView.vue +10 -2
- package/src/components/MobileSearch.vue +97 -0
- package/src/components/TableOfContents.vue +42 -6
- package/src/composables/useDocManager.js +134 -44
- package/src/composables/useDocTree.js +26 -50
- package/src/composables/useMarkdown.js +51 -140
- package/src/composables/useMermaidCache.js +15 -0
- package/src/composables/useScroll.js +317 -32
- package/src/composables/useSearch.js +12 -11
- package/src/config.js +1 -4
- package/src/services/DocService.js +0 -16
- package/src/style.css +235 -10
- package/src/utils/imageConverter.js +129 -0
- package/vite-plugin-doc-api.js +158 -157
- package/vite.config.js +5 -1
- package/src/components/SearchPanel.vue +0 -90
- package/src/components/TableBubbleMenu.vue +0 -177
- package/src/composables/useExportPdf.js +0 -102
package/README.md
CHANGED
|
@@ -4,27 +4,72 @@
|
|
|
4
4
|
[](https://github.com/xiaoyaodev/md2ui)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
轻量级 Markdown 文档站点工具,一行命令将本地 `.md` 文件转换为可预览、可编辑、可搜索的文档站点。
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## 为什么需要 md2ui?
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
11
|
+
AI 编程时代,Cursor、Copilot、Kiro 等工具正在改变我们的开发方式。与 AI 协作的过程中,会产生大量的 Markdown 文档 —— 需求文档、设计方案、API 文档、会议纪要、技术调研……
|
|
12
|
+
|
|
13
|
+
这些 `.md` 文件散落在项目各处,带来了一系列痛点:
|
|
14
|
+
|
|
15
|
+
- 文件越来越多,找一篇文档要翻半天
|
|
16
|
+
- IDE 的 Markdown 预览体验一般,表格、流程图、数学公式渲染不理想
|
|
17
|
+
- 想快速改几个字,还得切回编辑器找到对应文件
|
|
18
|
+
- 文档之间缺乏导航关系,无法形成知识体系
|
|
19
|
+
- 想分享给团队成员,还得额外搭建文档站
|
|
20
|
+
|
|
21
|
+
md2ui 就是为了解决这些问题而生的。把它指向你的文档目录,立刻获得一个功能完整的文档站点。
|
|
22
|
+
|
|
23
|
+
## 核心能力
|
|
24
|
+
|
|
25
|
+
- 零配置启动 - `cd docs && md2ui`,开箱即用
|
|
26
|
+
- 实时预览 - 文件变更自动刷新,所见即所得
|
|
27
|
+
- 在线编辑 - 内置富文本编辑器,直接在浏览器中修改并保存到本地文件
|
|
28
|
+
- 全文搜索 - 基于 MiniSearch,毫秒级检索所有文档内容
|
|
29
|
+
- 自动导航 - 扫描目录结构自动生成多级目录树,支持拖拽排序
|
|
30
|
+
- 三栏布局 - 左侧导航 / 中间内容 / 右侧大纲,宽度可拖拽调整
|
|
31
|
+
- Markdown 增强 - GFM 语法、代码高亮、Mermaid 图表、数学公式、Frontmatter
|
|
32
|
+
- 移动端适配 - 响应式布局,手机上也能舒适阅读
|
|
33
|
+
- 阅读体验 - 进度条、预计阅读时间、上下篇导航、图片放大
|
|
34
|
+
- SSG 构建 - `md2ui build` 生成纯静态站点,可直接部署
|
|
18
35
|
- 自定义配置 - 站点标题、主题色、GitHub 链接、页脚等
|
|
19
36
|
|
|
20
|
-
##
|
|
37
|
+
## 界面预览
|
|
38
|
+
预览模式
|
|
39
|
+

|
|
40
|
+
编辑模式
|
|
41
|
+
|
|
42
|
+

|
|
43
|
+
|
|
44
|
+
## 典型使用场景
|
|
45
|
+
|
|
46
|
+
**AI 辅助开发的文档管理**
|
|
47
|
+
|
|
48
|
+
用 AI 生成的需求文档、设计方案、技术调研统一放到一个目录,md2ui 提供即时预览和编辑,让文档真正流动起来。
|
|
49
|
+
|
|
50
|
+
**个人知识库**
|
|
21
51
|
|
|
22
|
-
|
|
52
|
+
日常笔记、学习记录、读书摘要,用 Markdown 写完丢进文件夹,md2ui 自动组织成可浏览的知识站点。
|
|
53
|
+
|
|
54
|
+
**项目文档站**
|
|
55
|
+
|
|
56
|
+
API 文档、部署指南、开发规范,`md2ui build` 一键构建为静态站点,部署到任意服务器。
|
|
57
|
+
|
|
58
|
+
**团队协作文档**
|
|
59
|
+
|
|
60
|
+
配合 Git 管理文档版本,本地用 md2ui 预览和编辑,提交后自动构建部署。
|
|
61
|
+
|
|
62
|
+
## 快速开始
|
|
63
|
+
|
|
64
|
+
### 安装
|
|
23
65
|
|
|
24
66
|
```bash
|
|
25
|
-
|
|
26
|
-
# 或
|
|
67
|
+
# 需要提前安装nodejs运行环境
|
|
27
68
|
npm install -g md2ui
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# 免安装 一次性运行
|
|
72
|
+
npx md2ui
|
|
28
73
|
```
|
|
29
74
|
|
|
30
75
|
### 实时预览
|
|
@@ -36,21 +81,19 @@ cd /path/to/your/docs
|
|
|
36
81
|
md2ui
|
|
37
82
|
```
|
|
38
83
|
|
|
39
|
-
|
|
84
|
+
访问 http://localhost:3000 即可查看文档。支持 `-p` 参数指定端口:
|
|
40
85
|
|
|
41
86
|
```bash
|
|
42
87
|
md2ui -p 8080
|
|
43
88
|
```
|
|
44
89
|
|
|
45
|
-
访问 http://localhost:3000 查看文档(默认端口 3000)。
|
|
46
|
-
|
|
47
90
|
### 静态构建
|
|
48
91
|
|
|
49
92
|
```bash
|
|
50
93
|
md2ui build
|
|
51
94
|
```
|
|
52
95
|
|
|
53
|
-
生成的静态文件在 `dist/`
|
|
96
|
+
生成的静态文件在 `dist/` 目录下,可直接部署到 Nginx、GitHub Pages、Vercel 等任意静态托管服务。
|
|
54
97
|
|
|
55
98
|
## 文档组织
|
|
56
99
|
|
|
@@ -65,8 +108,8 @@ your-docs/
|
|
|
65
108
|
```
|
|
66
109
|
|
|
67
110
|
- 使用 `序号-名称.md` 格式控制排序,如 `01-快速开始.md`
|
|
68
|
-
-
|
|
69
|
-
-
|
|
111
|
+
- 文件夹同样支持序号前缀,如 `02-进阶指南/`
|
|
112
|
+
- 支持任意层级嵌套
|
|
70
113
|
|
|
71
114
|
## 自定义配置
|
|
72
115
|
|
|
@@ -79,7 +122,7 @@ export default {
|
|
|
79
122
|
port: 8080,
|
|
80
123
|
folderExpanded: true,
|
|
81
124
|
themeColor: '#3eaf7c',
|
|
82
|
-
github: 'https://github.com/
|
|
125
|
+
github: 'https://github.com/your/repo',
|
|
83
126
|
footer: 'Copyright © 2025'
|
|
84
127
|
}
|
|
85
128
|
```
|
|
@@ -113,6 +156,8 @@ md2ui/
|
|
|
113
156
|
│ ├── App.vue # 主组件
|
|
114
157
|
│ ├── components/ # Vue 组件
|
|
115
158
|
│ ├── composables/ # 组合式函数
|
|
159
|
+
│ ├── extensions/ # Tiptap 编辑器扩展
|
|
160
|
+
│ ├── services/ # 文档服务
|
|
116
161
|
│ ├── config.js # 共享配置
|
|
117
162
|
│ └── style.css # 全局样式
|
|
118
163
|
├── public/docs/ # 示例文档
|
package/bin/build.js
CHANGED
|
@@ -574,8 +574,19 @@ function getSsgCss(pkgRoot) {
|
|
|
574
574
|
|
|
575
575
|
// ===== 主构建流程 =====
|
|
576
576
|
async function build() {
|
|
577
|
+
// 解析位置参数:md2ui build [dir]
|
|
578
|
+
const args = process.argv.slice(3)
|
|
579
|
+
let targetDir = null
|
|
580
|
+
for (const arg of args) {
|
|
581
|
+
if (!arg.startsWith('-')) {
|
|
582
|
+
targetDir = arg
|
|
583
|
+
break
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
const scanDir = targetDir ? resolve(userDir, targetDir) : userDir
|
|
587
|
+
|
|
577
588
|
console.log('\n md2ui build - 静态站点生成\n')
|
|
578
|
-
console.log(` 扫描目录: ${
|
|
589
|
+
console.log(` 扫描目录: ${scanDir}\n`)
|
|
579
590
|
|
|
580
591
|
// 加载配置
|
|
581
592
|
const userConfig = await loadUserConfig()
|
|
@@ -583,7 +594,7 @@ async function build() {
|
|
|
583
594
|
const outDir = resolve(userDir, siteConfig.outDir || 'dist')
|
|
584
595
|
|
|
585
596
|
// 扫描文档
|
|
586
|
-
const docsList = scanDocs(
|
|
597
|
+
const docsList = scanDocs(scanDir, '', 0, siteConfig.folderExpanded)
|
|
587
598
|
const flatDocs = flattenDocs(docsList)
|
|
588
599
|
|
|
589
600
|
if (flatDocs.length === 0) {
|
package/bin/md2ui.js
CHANGED
|
@@ -63,11 +63,14 @@ async function loadUserConfig() {
|
|
|
63
63
|
// 解析命令行参数
|
|
64
64
|
function parseArgs() {
|
|
65
65
|
const args = process.argv.slice(2)
|
|
66
|
-
const result = {}
|
|
66
|
+
const result = { dir: null }
|
|
67
67
|
for (let i = 0; i < args.length; i++) {
|
|
68
68
|
if (args[i] === '-p' || args[i] === '--port') {
|
|
69
69
|
result.port = parseInt(args[i + 1]) || undefined
|
|
70
70
|
i++
|
|
71
|
+
} else if (!args[i].startsWith('-')) {
|
|
72
|
+
// 位置参数作为扫描目录
|
|
73
|
+
result.dir = args[i]
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
76
|
return result
|
|
@@ -131,7 +134,7 @@ function hasMdFiles(dir) {
|
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
// Vite 插件:提供用户文档 API + 配置 API + 热更新
|
|
134
|
-
function md2uiPlugin(siteConfig) {
|
|
137
|
+
function md2uiPlugin(siteConfig, docsRoot) {
|
|
135
138
|
return {
|
|
136
139
|
name: 'md2ui-server',
|
|
137
140
|
configureServer(server) {
|
|
@@ -139,7 +142,7 @@ function md2uiPlugin(siteConfig) {
|
|
|
139
142
|
server.middlewares.use((req, res, next) => {
|
|
140
143
|
// 文档列表 API(带 ETag 支持,避免轮询时重复传输)
|
|
141
144
|
if (req.url === '/@user-docs-list') {
|
|
142
|
-
const docs = scanDocs(
|
|
145
|
+
const docs = scanDocs(docsRoot, '', 0, siteConfig.folderExpanded)
|
|
143
146
|
const body = JSON.stringify(docs)
|
|
144
147
|
const etag = '"' + crypto.createHash('md5').update(body).digest('hex') + '"'
|
|
145
148
|
if (req.headers['if-none-match'] === etag) {
|
|
@@ -172,8 +175,8 @@ function md2uiPlugin(siteConfig) {
|
|
|
172
175
|
res.statusCode = 400; res.end('缺少文档路径'); return
|
|
173
176
|
}
|
|
174
177
|
|
|
175
|
-
const docDir = dirname(resolve(
|
|
176
|
-
if (!docDir.startsWith(
|
|
178
|
+
const docDir = dirname(resolve(docsRoot, docPath))
|
|
179
|
+
if (!docDir.startsWith(docsRoot)) {
|
|
177
180
|
res.statusCode = 403; res.end('禁止访问'); return
|
|
178
181
|
}
|
|
179
182
|
|
|
@@ -187,7 +190,7 @@ function md2uiPlugin(siteConfig) {
|
|
|
187
190
|
|
|
188
191
|
fs.writeFileSync(targetPath, body)
|
|
189
192
|
|
|
190
|
-
//
|
|
193
|
+
// 返回相对于文档根目录的路径(对路径各段做 URL 编码)
|
|
191
194
|
const docDirRel = dirname(docPath)
|
|
192
195
|
const encodedDir = docDirRel && docDirRel !== '.'
|
|
193
196
|
? docDirRel.split('/').map(encodeURIComponent).join('/')
|
|
@@ -206,7 +209,7 @@ function md2uiPlugin(siteConfig) {
|
|
|
206
209
|
}
|
|
207
210
|
// 文档内容
|
|
208
211
|
if (req.url?.startsWith('/@user-docs/')) {
|
|
209
|
-
const filePath = resolve(
|
|
212
|
+
const filePath = resolve(docsRoot, decodeURIComponent(req.url.replace('/@user-docs/', '')))
|
|
210
213
|
if (fs.existsSync(filePath)) {
|
|
211
214
|
// 根据扩展名设置 Content-Type
|
|
212
215
|
const extName = filePath.split('.').pop().toLowerCase()
|
|
@@ -254,10 +257,14 @@ function md2uiPlugin(siteConfig) {
|
|
|
254
257
|
}
|
|
255
258
|
|
|
256
259
|
async function start() {
|
|
260
|
+
// 解析参数,支持位置参数指定扫描目录
|
|
261
|
+
const cliArgs = parseArgs()
|
|
262
|
+
const scanDir = cliArgs.dir ? resolve(userDir, cliArgs.dir) : userDir
|
|
263
|
+
|
|
257
264
|
console.log(`\n md2ui - Markdown 文档预览工具\n`)
|
|
258
|
-
console.log(` 扫描目录: ${
|
|
265
|
+
console.log(` 扫描目录: ${scanDir}\n`)
|
|
259
266
|
|
|
260
|
-
if (!hasMdFiles(
|
|
267
|
+
if (!hasMdFiles(scanDir)) {
|
|
261
268
|
console.log(' 当前目录下没有找到 Markdown 文件 (.md)\n')
|
|
262
269
|
console.log(' 请在包含 .md 文件的目录中运行此命令\n')
|
|
263
270
|
process.exit(1)
|
|
@@ -265,15 +272,21 @@ async function start() {
|
|
|
265
272
|
|
|
266
273
|
// 加载配置
|
|
267
274
|
const userConfig = await loadUserConfig()
|
|
268
|
-
const cliArgs = parseArgs()
|
|
269
275
|
const siteConfig = { ...defaultConfig, ...userConfig, ...cliArgs }
|
|
276
|
+
delete siteConfig.dir
|
|
270
277
|
|
|
271
278
|
const server = await createServer({
|
|
272
279
|
root: pkgRoot,
|
|
273
|
-
configFile:
|
|
274
|
-
plugins: [
|
|
280
|
+
configFile: false,
|
|
281
|
+
plugins: [
|
|
282
|
+
(await import('@vitejs/plugin-vue')).default(),
|
|
283
|
+
md2uiPlugin(siteConfig, scanDir)
|
|
284
|
+
],
|
|
275
285
|
server: {
|
|
276
286
|
port: siteConfig.port
|
|
287
|
+
},
|
|
288
|
+
optimizeDeps: {
|
|
289
|
+
include: ['vue', 'marked', 'mermaid']
|
|
277
290
|
}
|
|
278
291
|
})
|
|
279
292
|
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "md2ui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.21",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "将本地 Markdown 文档转换为美观的 HTML 页面",
|
|
6
6
|
"author": "",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "https://github.com/xiaoyaodev/md2ui"
|
|
10
|
+
"url": "git+https://github.com/xiaoyaodev/md2ui.git"
|
|
11
11
|
},
|
|
12
12
|
"keywords": [
|
|
13
13
|
"markdown",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"vue"
|
|
18
18
|
],
|
|
19
19
|
"bin": {
|
|
20
|
-
"md2ui": "
|
|
20
|
+
"md2ui": "bin/md2ui.js"
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
23
|
"dev": "vite",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"katex": "^0.16.45",
|
|
55
55
|
"lucide-vue-next": "^0.556.0",
|
|
56
56
|
"marked": "^11.1.1",
|
|
57
|
-
"mermaid": "^
|
|
57
|
+
"mermaid": "^11.14.0",
|
|
58
58
|
"minisearch": "^7.2.0",
|
|
59
59
|
"tiptap-markdown": "^0.9.0",
|
|
60
60
|
"vite": "^5.0.0",
|