@movk/nuxt-docs 1.15.0 → 1.16.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.
package/README.md CHANGED
@@ -1,3 +1,4 @@
1
+ [![Movk Nuxt Docs OG](https://docs.mhaibaraai.cn/_og/s/c_Docs,title_~546w5Luj5YyWIE51eHQg5paH5qGj5Li76aKY,description_~5Z-65LqOIE51eHQgNCDnmoTnjrDku6PmlofmoaPkuLvpopjvvIzpm4bmiJDnu4Tku7boh6rliqjljJbmlofmoaPjgIFBSSDogYrlpKnliqnmiYvjgIFNQ1AgU2VydmVyIOWSjOWujOaVtOeahOW8gOWPkeiAheS9k-mqjOS8mOWMluOAgg.png)](https://docs.mhaibaraai.cn/)
1
2
  [![Movk Nuxt Docs](https://docs.mhaibaraai.cn/og-image.png)](https://docs.mhaibaraai.cn/)
2
3
 
3
4
  > 基于 Nuxt 4 的现代文档主题,集成组件自动化文档、AI 聊天助手、MCP Server 和完整的开发者体验优化
@@ -21,7 +22,7 @@
21
22
  ### 🤖 AI 增强体验
22
23
 
23
24
  <div style="padding: 40px 0; display: flex; justify-content: center;">
24
- <img src="https://docs.mhaibaraai.cn/ai/AiChat.png" alt="AiChat" width="400">
25
+ <img src="https://docs.mhaibaraai.cn/ai/AiChat.png" alt="AiChat" width="400">
25
26
  </div>
26
27
 
27
28
  - **AI 聊天助手** - 内置智能文档助手,基于 Vercel AI SDK 支持多种 LLM 模型
@@ -189,7 +190,10 @@ pnpm add mermaid dompurify
189
190
  ```ts [nuxt.config.ts]
190
191
  export default defineNuxtConfig({
191
192
  extends: ['@movk/nuxt-docs'],
192
- mermaid: { enabled: true }
193
+
194
+ movkNuxtDocs: {
195
+ mermaid: true
196
+ }
193
197
  })
194
198
  ```
195
199
 
@@ -198,11 +202,11 @@ export default defineNuxtConfig({
198
202
  ````md [md]
199
203
  ```mermaid
200
204
  graph TD
201
- A[开始] --> B{是否有效?}
202
- B -->|是| C[处理数据]
203
- B -->|否| D[显示错误]
204
- C --> E[完成]
205
- D --> E
205
+ A[开始] --> B{是否有效?}
206
+ B -->|是| C[处理数据]
207
+ B -->|否| D[显示错误]
208
+ C --> E[完成]
209
+ D --> E
206
210
  ```
207
211
  ````
208
212
 
@@ -215,9 +219,9 @@ graph TD
215
219
 
216
220
  **支持的图表类型:**
217
221
  - **流程图**(`flowchart`/`graph`):用于展示流程和决策
218
- ![Mermaid 流程图示例](https://docs.mhaibaraai.cn/mermaid/mermaid-flowchart.png)
222
+ ![Mermaid 流程图示例](https://docs.mhaibaraai.cn/mermaid/mermaid-flowchart.png)
219
223
  - **时序图**(`sequenceDiagram`):用于展示交互时序
220
- ![Mermaid 时序图示例](https://docs.mhaibaraai.cn/mermaid/mermaid-sequence.png)
224
+ ![Mermaid 时序图示例](https://docs.mhaibaraai.cn/mermaid/mermaid-sequence.png)
221
225
  - **类图**(`classDiagram`):用于展示类关系
222
226
  - **状态图**(`stateDiagram`):用于展示状态转换
223
227
  - **甘特图**(`gantt`):用于展示项目时间线
@@ -225,6 +229,20 @@ graph TD
225
229
  - **Git 图**(`gitGraph`):用于展示分支历史
226
230
  - 以及更多 [Mermaid 支持的图表类型](https://mermaid.js.org/intro())
227
231
 
232
+ ### 无障碍支持(A11y)
233
+
234
+ Movk Nuxt Docs 默认启用 `@nuxt/a11y`。如需关闭,可在 `movkNuxtDocs` 中设置:
235
+
236
+ ```ts [nuxt.config.ts]
237
+ export default defineNuxtConfig({
238
+ extends: ['@movk/nuxt-docs'],
239
+
240
+ movkNuxtDocs: {
241
+ a11y: false
242
+ }
243
+ })
244
+ ```
245
+
228
246
  ## 🛠️ 开发
229
247
 
230
248
  ### 本地开发
@@ -11,10 +11,7 @@ withDefaults(defineProps<{
11
11
  </script>
12
12
 
13
13
  <template>
14
- <div
15
- style="font-family: 'Public Sans', 'Noto Sans SC', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;"
16
- class="w-full h-full flex flex-col justify-center items-center relative p-10 lg:p-15 bg-white text-neutral-900 dark:bg-neutral-900 dark:text-neutral-50"
17
- >
14
+ <div style="font-family: 'Noto Sans SC', sans-serif;" class="w-full h-full flex flex-col justify-center items-center relative p-10 lg:p-15 bg-white text-neutral-900 dark:bg-neutral-900 dark:text-neutral-50">
18
15
  <div
19
16
  class="absolute top-0 left-0 right-0 bottom-0"
20
17
  :style="{
@@ -82,7 +79,10 @@ withDefaults(defineProps<{
82
79
  </h1>
83
80
  </div>
84
81
 
85
- <p v-if="description" class="text-slate-500 text-[24px] lg:text-[32px] opacity-70 max-w-162.5 lg:max-w-225 leading-normal">
82
+ <p
83
+ v-if="description"
84
+ class="text-slate-500 text-[24px] lg:text-[32px] opacity-70 max-w-162.5 lg:max-w-225 leading-normal"
85
+ >
86
86
  {{ description }}
87
87
  </p>
88
88
  </div>
@@ -98,8 +98,11 @@ function getToolIcon(part: ToolPart): string {
98
98
  const { toolIcon } = useToolCall(part.state, toolName, part.input || {} as any)
99
99
 
100
100
  const iconMap: Record<string, string> = {
101
- 'get-page': 'i-lucide-file-text',
102
- 'get-example': 'i-lucide-file-text',
101
+ 'get-page': 'i-lucide-book-open',
102
+ 'get-example': 'i-lucide-codepen',
103
+ 'list-examples': 'i-lucide-codesandbox',
104
+ 'list-getting-started-guides': 'i-lucide-square-play',
105
+ 'list-pages': 'i-lucide-book-minus',
103
106
  ...toolIcon
104
107
  }
105
108
 
package/modules/config.ts CHANGED
@@ -1,27 +1,9 @@
1
- import { existsSync } from 'node:fs'
2
1
  import { createResolver, defineNuxtModule } from '@nuxt/kit'
3
2
  import { defu } from 'defu'
4
- import { join } from 'pathe'
5
3
  import { getGitBranch, getGitEnv, getLocalGitInfo } from '../utils/git'
6
4
  import { getPackageJsonMetadata, inferSiteURL } from '../utils/meta'
7
5
  import { createComponentMetaExcludeFilters } from '../utils/component-meta'
8
6
 
9
- function getMdcConfigSources(nuxt: any): string[] {
10
- return nuxt.options._layers.flatMap((layer: any) => {
11
- const tsConfigPath = join(layer.config.srcDir, 'mdc.config.ts')
12
- if (existsSync(tsConfigPath)) {
13
- return [tsConfigPath]
14
- }
15
-
16
- const jsConfigPath = join(layer.config.srcDir, 'mdc.config.js')
17
- if (existsSync(jsConfigPath)) {
18
- return [jsConfigPath]
19
- }
20
-
21
- return []
22
- })
23
- }
24
-
25
7
  export default defineNuxtModule({
26
8
  meta: {
27
9
  name: 'config'
@@ -91,10 +73,12 @@ export default defineNuxtModule({
91
73
  ]
92
74
  })
93
75
 
94
- // Load mdc.config from all layers
95
- const mdcConfigSources = getMdcConfigSources(nuxt)
96
- if (mdcConfigSources.length > 0) {
97
- await nuxt.callHook('mdc:configSources', mdcConfigSources)
98
- }
76
+ nuxt.hook('nitro:config', (nitroConfig) => {
77
+ nitroConfig.publicAssets ||= []
78
+ nitroConfig.publicAssets.push({
79
+ dir: resolve('./runtime/public'),
80
+ maxAge: 60 * 60 * 24 * 30
81
+ })
82
+ })
99
83
  }
100
84
  })
@@ -0,0 +1,103 @@
1
+ import { addComponentsDir, createResolver, defineNuxtModule, logger } from '@nuxt/kit'
2
+ import type { ModuleDependencies } from 'nuxt/schema'
3
+
4
+ export interface ModuleOptions {
5
+ /**
6
+ * 是否启用 @nuxt/a11y 无障碍支持
7
+ * @defaultValue true
8
+ * @see https://a11y.nuxtjs.org/
9
+ */
10
+ a11y?: boolean
11
+ /**
12
+ * 是否启用 Mermaid 图表支持
13
+ * - 启用前需安装依赖: `pnpm add mermaid dompurify`
14
+ * @defaultValue false
15
+ */
16
+ mermaid?: boolean
17
+ }
18
+
19
+ const log = logger.withTag('movk-nuxt-docs')
20
+
21
+ export default defineNuxtModule<ModuleOptions>({
22
+ meta: {
23
+ name: 'movk-nuxt-docs',
24
+ configKey: 'movkNuxtDocs'
25
+ },
26
+ defaults: {
27
+ a11y: true,
28
+ mermaid: false
29
+ },
30
+ moduleDependencies(nuxt): ModuleDependencies {
31
+ const userOptions = nuxt.options.movkNuxtDocs || {}
32
+ return {
33
+ ...userOptions.a11y !== false && {
34
+ '@nuxt/a11y': {
35
+ version: '^1.0.0-alpha.1'
36
+ }
37
+ }
38
+ }
39
+ },
40
+ setup(options, nuxt) {
41
+ const { resolve } = createResolver(import.meta.url)
42
+
43
+ if (options.mermaid) {
44
+ let mermaidAvailable = true
45
+ try {
46
+ import.meta.resolve('mermaid')
47
+ } catch {
48
+ log.warn('mermaid package not found. install it with: pnpm add mermaid dompurify')
49
+ mermaidAvailable = false
50
+ }
51
+
52
+ if (mermaidAvailable) {
53
+ addComponentsDir({
54
+ path: resolve('./runtime/components/prose'),
55
+ pathPrefix: false,
56
+ prefix: 'Prose',
57
+ global: true
58
+ })
59
+
60
+ // 添加 mermaid 语言高亮
61
+ const contentOptions = nuxt.options.content as Record<string, any> | false
62
+ if (contentOptions) {
63
+ const build = contentOptions.build ??= {}
64
+ const markdown = build.markdown ??= {}
65
+ const highlight = markdown.highlight ??= {}
66
+ const langs = highlight.langs ??= [] as string[]
67
+ if (!langs.includes('mermaid')) {
68
+ langs.push('mermaid')
69
+ }
70
+ }
71
+
72
+ // 为 mermaid ESM 兼容性配置 optimizeDeps
73
+ nuxt.hooks.hook('vite:extendConfig', (config) => {
74
+ const cfg = config as { optimizeDeps?: { include?: string[] } }
75
+ cfg.optimizeDeps ??= {}
76
+ cfg.optimizeDeps.include ??= []
77
+ cfg.optimizeDeps.include.push(
78
+ '@movk/nuxt-docs > mermaid',
79
+ '@movk/nuxt-docs > mermaid > dayjs',
80
+ '@movk/nuxt-docs > mermaid > @braintree/sanitize-url',
81
+ '@movk/nuxt-docs > mermaid > d3',
82
+ '@movk/nuxt-docs > mermaid > dompurify'
83
+ )
84
+ })
85
+
86
+ // 注入 mermaid 代码图标配置到 appConfig
87
+ const appConfig = nuxt.options.appConfig as Record<string, any>
88
+ appConfig.ui ??= {}
89
+ appConfig.ui.prose ??= {}
90
+ appConfig.ui.prose.codeIcon ??= {}
91
+ appConfig.ui.prose.codeIcon.mmd ??= 'i-vscode-icons-file-type-mermaid'
92
+
93
+ log.info('mermaid diagram support enabled')
94
+ }
95
+ }
96
+ }
97
+ })
98
+
99
+ declare module 'nuxt/schema' {
100
+ interface NuxtConfig {
101
+ movkNuxtDocs?: ModuleOptions
102
+ }
103
+ }
@@ -1,16 +1,13 @@
1
1
  <script setup lang="ts">
2
2
  import type { ProsePreProps } from '@nuxt/ui'
3
- // @ts-ignore
3
+ // @ts-ignore - #build alias only available at Nuxt build time
4
4
  import NuxtUIProsePre from '@nuxt/ui/components/prose/Pre.vue'
5
5
 
6
6
  const props = defineProps<ProsePreProps>()
7
7
 
8
- const isMermaid = computed(() => props.language === 'mermaid')
9
-
10
- // 动态解析 Mermaid 组件(仅在 mermaid 模块启用时可用)
11
8
  const MermaidComponent = computed(() => {
12
- if (!isMermaid.value) return null
13
- const resolved = resolveComponent('Mermaid')
9
+ if (props.language !== 'mermaid') return null
10
+ const resolved = resolveComponent('ProseMermaid')
14
11
  return typeof resolved === 'string' ? null : resolved
15
12
  })
16
13
  </script>
@@ -18,11 +15,12 @@ const MermaidComponent = computed(() => {
18
15
  <template>
19
16
  <component
20
17
  :is="MermaidComponent"
21
- v-if="isMermaid && MermaidComponent"
18
+ v-if="MermaidComponent"
22
19
  :code="props.code || ''"
23
20
  :filename="props.filename"
24
21
  :icon="props.icon"
25
22
  />
23
+
26
24
  <NuxtUIProsePre v-else v-bind="props">
27
25
  <slot />
28
26
  </NuxtUIProsePre>
package/nuxt.config.ts CHANGED
@@ -20,7 +20,6 @@ export default defineNuxtConfig({
20
20
  '@nuxt/fonts',
21
21
  '@nuxt/content',
22
22
  '@nuxt/image',
23
- '@nuxt/a11y',
24
23
  '@nuxtjs/robots',
25
24
  '@nuxtjs/mcp-toolkit',
26
25
  '@vueuse/nuxt',
@@ -172,10 +171,9 @@ export default defineNuxtConfig({
172
171
  },
173
172
 
174
173
  fonts: {
175
- provider: 'bunny',
176
174
  families: [
177
- { name: 'Public Sans', global: true, fallbacks: ['Noto Sans SC', 'sans-serif'] },
178
- { name: 'Noto Sans SC', global: true }
175
+ { name: 'Public Sans', global: true },
176
+ { name: 'Noto Sans SC', global: true, provider: 'local' }
179
177
  ]
180
178
  },
181
179
 
@@ -196,5 +194,9 @@ export default defineNuxtConfig({
196
194
  // Must be defined before @nuxt/content setup,
197
195
  // otherwise Content LLMS module will overwrite it in modules:done.
198
196
  contentRawMarkdown: false
197
+ },
198
+
199
+ ogImage: {
200
+ zeroRuntime: true
199
201
  }
200
202
  })
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@movk/nuxt-docs",
3
3
  "type": "module",
4
- "version": "1.15.0",
4
+ "version": "1.16.1",
5
5
  "private": false,
6
6
  "description": "Modern Nuxt 4 documentation theme with auto-generated component docs, AI chat assistant, MCP server, and complete developer experience optimization.",
7
7
  "author": "YiXuan <mhaibaraai@gmail.com>",
@@ -39,10 +39,10 @@
39
39
  "README.md"
40
40
  ],
41
41
  "dependencies": {
42
- "@ai-sdk/gateway": "^3.0.79",
42
+ "@ai-sdk/gateway": "^3.0.83",
43
43
  "@ai-sdk/mcp": "^1.0.30",
44
- "@ai-sdk/vue": "^3.0.137",
45
- "@iconify-json/lucide": "^1.2.98",
44
+ "@ai-sdk/vue": "^3.0.141",
45
+ "@iconify-json/lucide": "^1.2.100",
46
46
  "@iconify-json/simple-icons": "^1.2.75",
47
47
  "@iconify-json/vscode-icons": "^1.2.45",
48
48
  "@movk/core": "^1.2.2",
@@ -52,8 +52,8 @@
52
52
  "@nuxt/kit": "^4.4.2",
53
53
  "@nuxt/ui": "^4.6.0",
54
54
  "@nuxtjs/mcp-toolkit": "^0.12.0",
55
- "@nuxtjs/mdc": "^0.20.2",
56
- "@nuxtjs/robots": "^5.7.1",
55
+ "@nuxtjs/mdc": "^0.21.0",
56
+ "@nuxtjs/robots": "^6.0.6",
57
57
  "@octokit/rest": "^22.0.1",
58
58
  "@shikijs/core": "^4.0.2",
59
59
  "@shikijs/engine-javascript": "^4.0.2",
@@ -63,14 +63,14 @@
63
63
  "@takumi-rs/wasm": "^0.73.1",
64
64
  "@vueuse/core": "^14.2.1",
65
65
  "@vueuse/nuxt": "^14.2.1",
66
- "ai": "^6.0.137",
66
+ "ai": "^6.0.141",
67
67
  "defu": "^6.1.4",
68
68
  "exsolve": "^1.0.8",
69
69
  "git-url-parse": "^16.1.0",
70
70
  "motion-v": "^2.2.0",
71
71
  "nuxt-component-meta": "^0.17.2",
72
72
  "nuxt-llms": "^0.2.0",
73
- "nuxt-og-image": "^6.1.2",
73
+ "nuxt-og-image": "^6.3.0",
74
74
  "ohash": "^2.0.11",
75
75
  "pathe": "^2.0.3",
76
76
  "pkg-types": "^2.3.0",
@@ -13,7 +13,7 @@ export default defineMcpTool({
13
13
  content: [{ type: 'text' as const, text: result.code }]
14
14
  }
15
15
  } catch {
16
- return errorResult(`示例 '${exampleName}' 未找到。使用 list_examples 工具查看所有可用示例。`)
16
+ throw createError({ statusCode: 404, message: `示例 '${exampleName}' 未找到。使用 list_examples 工具查看所有可用示例。` })
17
17
  }
18
18
  }
19
19
  })
@@ -22,7 +22,7 @@ export default defineMcpTool({
22
22
  content: [{ type: 'text', text: JSON.stringify(content, null, 2) }]
23
23
  }
24
24
  } catch (error) {
25
- return errorResult(`获取页面失败: ${error}`)
25
+ throw createError({ statusCode: 500, message: `获取页面失败: ${error}` })
26
26
  }
27
27
  }
28
28
  })
@@ -5,6 +5,6 @@ export default defineMcpTool({
5
5
  description: '列出所有可用的示例和代码演示',
6
6
  cache: '1h',
7
7
  async handler() {
8
- return jsonResult(await listComponentExamples())
8
+ return await listComponentExamples()
9
9
  }
10
10
  })
@@ -22,6 +22,6 @@ export default defineMcpTool({
22
22
  navigation: page.navigation
23
23
  })).sort((a, b) => a.path.localeCompare(b.path))
24
24
 
25
- return jsonResult(result)
25
+ return result
26
26
  }
27
27
  })
@@ -8,10 +8,10 @@ export default defineMcpTool({
8
8
 
9
9
  const pages = await queryCollection(event, 'docs').all()
10
10
 
11
- return jsonResult(pages.map(doc => ({
11
+ return pages.map(doc => ({
12
12
  title: doc.title,
13
13
  description: doc.description,
14
14
  path: doc.path
15
- })))
15
+ }))
16
16
  }
17
17
  })
@@ -52,11 +52,7 @@ export function createComponentMetaExcludeFilters(
52
52
  resolve('../app/components/content/ComponentProps.vue'),
53
53
  resolve('../app/components/content/ComponentSlots.vue'),
54
54
  resolve('../app/components/content/PageLastCommit.vue'),
55
- resolve('../app/components/content/Mermaid.vue'),
56
- resolve('./ai-chat/runtime/components/AiChatToolCall.vue'),
57
- resolve('./ai-chat/runtime/components/AiChatReasoning.vue'),
58
- resolve('./ai-chat/runtime/components/AiChatSlideoverFaq.vue'),
59
- resolve('./ai-chat/runtime/components/AiChatPreStream.vue')
55
+ resolve('./runtime/components/prose/Mermaid.vue')
60
56
  ]
61
57
 
62
58
  const userComponentPaths = [
@@ -1,93 +0,0 @@
1
- import { createResolver, defineNuxtModule, logger } from '@nuxt/kit'
2
-
3
- export interface MermaidModuleOptions {
4
- /**
5
- * 是否启用 Mermaid 图表支持
6
- *
7
- * 启用前需安装依赖: `pnpm add mermaid dompurify`
8
- * @default false
9
- */
10
- enabled?: boolean
11
- }
12
-
13
- const log = logger.withTag('movk-nuxt-docs')
14
-
15
- export default defineNuxtModule<MermaidModuleOptions>({
16
- meta: {
17
- name: 'mermaid',
18
- configKey: 'mermaid'
19
- },
20
- defaults: {
21
- enabled: false
22
- },
23
- setup(options, nuxt) {
24
- const { resolve } = createResolver(import.meta.url)
25
- const mermaidFilePath = resolve('../app/components/content/Mermaid.vue')
26
-
27
- // Layer 自动扫描会注册 Mermaid.vue,通过 components:extend 统一管理:
28
- // - 禁用时:从列表移除,resolveComponent('Mermaid') 返回字符串,ProsePre fallback 到普通代码块
29
- // - 启用时:将 mode 改为 'client',避免 SSR 阶段执行
30
- nuxt.hooks.hook('components:extend', (components) => {
31
- const idx = components.findIndex(c => c.filePath === mermaidFilePath)
32
- if (idx === -1) return
33
-
34
- if (!options.enabled) {
35
- components.splice(idx, 1)
36
- return
37
- }
38
-
39
- components[idx]!.mode = 'client'
40
- })
41
-
42
- if (!options.enabled) return
43
-
44
- // 验证 mermaid 依赖是否已安装
45
- try {
46
- import.meta.resolve('mermaid')
47
- } catch {
48
- log.warn('[mermaid] `mermaid` package not found. Install it with: pnpm add mermaid dompurify')
49
- return
50
- }
51
-
52
- // 添加 mermaid 语言高亮
53
- const contentOptions = nuxt.options.content as Record<string, any> | false
54
- if (contentOptions) {
55
- const build = contentOptions.build ??= {}
56
- const markdown = build.markdown ??= {}
57
- const highlight = markdown.highlight ??= {}
58
- const langs = highlight.langs ??= [] as string[]
59
- if (!langs.includes('mermaid')) {
60
- langs.push('mermaid')
61
- }
62
- }
63
-
64
- // 为 mermaid ESM 兼容性配置 optimizeDeps
65
- nuxt.hooks.hook('vite:extendConfig', (config) => {
66
- const cfg = config as { optimizeDeps?: { include?: string[] } }
67
- cfg.optimizeDeps ??= {}
68
- cfg.optimizeDeps.include ??= []
69
- cfg.optimizeDeps.include.push(
70
- '@movk/nuxt-docs > mermaid',
71
- '@movk/nuxt-docs > mermaid > dayjs',
72
- '@movk/nuxt-docs > mermaid > @braintree/sanitize-url',
73
- '@movk/nuxt-docs > mermaid > d3',
74
- '@movk/nuxt-docs > mermaid > dompurify'
75
- )
76
- })
77
-
78
- // 注入 mermaid 代码图标配置到 appConfig
79
- const appConfig = nuxt.options.appConfig as Record<string, any>
80
- appConfig.ui ??= {}
81
- appConfig.ui.prose ??= {}
82
- appConfig.ui.prose.codeIcon ??= {}
83
- appConfig.ui.prose.codeIcon.mmd ??= 'i-vscode-icons-file-type-mermaid'
84
-
85
- log.info('[mermaid] Mermaid diagram support enabled')
86
- }
87
- })
88
-
89
- declare module 'nuxt/schema' {
90
- interface NuxtConfig {
91
- mermaid?: MermaidModuleOptions
92
- }
93
- }