@movk/nuxt-docs 1.5.1 → 1.6.0

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 (53) hide show
  1. package/README.md +54 -24
  2. package/app/components/DocsAsideLeftBody.vue +13 -0
  3. package/app/components/DocsAsideLeftTop.vue +6 -0
  4. package/app/components/DocsAsideRightBottom.vue +29 -0
  5. package/app/components/OgImage/Nuxt.vue +40 -16
  6. package/app/components/PageHeaderLinks.vue +35 -9
  7. package/app/components/content/CommitChangelog.vue +34 -10
  8. package/app/components/content/ComponentEmits.vue +2 -2
  9. package/app/components/content/ComponentExample.vue +10 -10
  10. package/app/components/content/ComponentProps.vue +5 -3
  11. package/app/components/content/ComponentPropsSchema.vue +5 -1
  12. package/app/components/content/ComponentSlots.vue +2 -2
  13. package/app/components/content/HighlightInlineType.vue +1 -1
  14. package/app/components/content/PageLastCommit.vue +12 -8
  15. package/app/components/footer/Footer.vue +22 -0
  16. package/app/components/footer/FooterLeft.vue +7 -0
  17. package/app/components/footer/FooterRight.vue +14 -0
  18. package/app/components/header/Header.vue +4 -7
  19. package/app/components/header/HeaderCTA.vue +18 -0
  20. package/app/components/header/HeaderCenter.vue +7 -0
  21. package/app/components/theme-picker/ThemePicker.vue +24 -201
  22. package/app/composables/useFaq.ts +21 -0
  23. package/app/composables/useHighlighter.ts +22 -0
  24. package/app/composables/useTheme.ts +223 -0
  25. package/app/layouts/docs.vue +2 -15
  26. package/app/pages/docs/[...slug].vue +1 -1
  27. package/app/types/index.d.ts +6 -0
  28. package/app/utils/shiki-transformer-icon-highlight.ts +1 -1
  29. package/app/utils/unicode.ts +12 -0
  30. package/content.config.ts +2 -1
  31. package/modules/ai-chat/index.ts +90 -0
  32. package/modules/ai-chat/runtime/components/AiChat.vue +22 -0
  33. package/modules/ai-chat/runtime/components/AiChatFloatingInput.vue +85 -0
  34. package/modules/ai-chat/runtime/components/AiChatModelSelect.vue +24 -0
  35. package/modules/ai-chat/runtime/components/AiChatPreStream.vue +58 -0
  36. package/modules/ai-chat/runtime/components/AiChatReasoning.vue +49 -0
  37. package/modules/ai-chat/runtime/components/AiChatSlideover.vue +245 -0
  38. package/modules/ai-chat/runtime/components/AiChatSlideoverFaq.vue +41 -0
  39. package/modules/ai-chat/runtime/components/AiChatToolCall.vue +31 -0
  40. package/modules/ai-chat/runtime/composables/useAIChat.ts +45 -0
  41. package/modules/ai-chat/runtime/composables/useModels.ts +58 -0
  42. package/modules/ai-chat/runtime/composables/useTools.ts +31 -0
  43. package/modules/ai-chat/runtime/server/api/search.ts +84 -0
  44. package/modules/ai-chat/runtime/server/utils/docs_agent.ts +49 -0
  45. package/modules/ai-chat/runtime/server/utils/getModel.ts +25 -0
  46. package/modules/css.ts +2 -0
  47. package/nuxt.config.ts +27 -39
  48. package/package.json +16 -6
  49. package/server/mcp/tools/get-page.ts +60 -0
  50. package/server/mcp/tools/list-pages.ts +49 -0
  51. package/app/components/AdsCarbon.vue +0 -3
  52. package/app/components/Footer.vue +0 -32
  53. package/modules/llms.ts +0 -27
package/README.md CHANGED
@@ -1,13 +1,16 @@
1
- [![Movk Nuxt Docs](https://docs.mhaibaraai.cn/__og-image__/static/og.png)](https://docs.mhaibaraai.cn/)
1
+ [![Movk Nuxt Docs](https://docs.mhaibaraai.cn/og-image.png)](https://docs.mhaibaraai.cn/)
2
2
 
3
- > 一款由 Nuxt UI Nuxt Content 强力驱动的优雅文档主题
3
+ > 基于 Nuxt 4 的现代文档主题,集成组件自动化文档、AI 聊天助手、MCP Server 和完整的开发者体验优化
4
+
5
+ [![Install MCP in Cursor](https://docs.mhaibaraai.cn/mcp/badge.svg)](https://docs.mhaibaraai.cn/mcp/deeplink)
6
+ [![Install MCP in VS Code](https://docs.mhaibaraai.cn/mcp/badge.svg?ide=vscode)](https://docs.mhaibaraai.cn/mcp/deeplink?ide=vscode)
4
7
 
5
8
  [![npm version][npm-version-src]][npm-version-href]
6
9
  [![npm downloads][npm-downloads-src]][npm-downloads-href]
7
10
  [![License][license-src]][license-href]
8
11
  [![Nuxt][nuxt-src]][nuxt-href]
9
12
 
10
- 使用此主题可以快速构建美观、专业的文档网站,内置内容管理、SEO、暗黑模式、全文搜索等功能。
13
+ 使用此主题可以快速构建美观、专业、智能的文档网站,内置组件文档自动生成、AI 聊天助手、MCP Server 支持、SEO 优化、暗黑模式、全文搜索等功能。
11
14
 
12
15
  - 📖 [在线文档](https://docs.mhaibaraai.cn/)
13
16
 
@@ -15,18 +18,30 @@
15
18
 
16
19
  此主题集成了一系列旨在优化文档管理体验的强大功能:
17
20
 
21
+ ### 🤖 AI 增强体验
22
+
23
+ - **AI 聊天助手** - 内置智能文档助手,基于 Vercel AI SDK 支持多种 LLM 模型(Mistral、Qwen、OpenRouter)
24
+ - **MCP Server 支持** - 集成 Model Context Protocol 服务器,为 AI 助手提供结构化的文档访问能力
25
+ - **LLM 优化** - 通过 `nuxt-llms` 模块自动生成 `llms.txt` 和 `llms-full.txt`,为 AI 工具提供优化的文档索引
26
+ - **流式响应** - 支持 AI 响应流式输出和代码高亮,配合 `shiki-stream` 实现实时语法高亮渲染
27
+
28
+ ### 🧩 自动化文档生成
29
+
30
+ - **组件元数据自动提取** - 基于 `nuxt-component-meta` 自动提取 Vue 组件的 Props、Slots、Emits 定义
31
+ - **交互式示例展示** - 通过 `ComponentExample` 组件自动加载和渲染组件示例,支持代码高亮和实时预览
32
+ - **Git 提交历史集成** - 使用 `CommitChangelog` 和 `PageLastCommit` 组件自动展示文件的提交历史记录
33
+ - **类型定义高亮** - 智能解析 TypeScript 类型定义,支持内联类型高亮和类型导航
34
+
35
+ ### 🎨 开发者体验
36
+
18
37
  - ⚡ **基于 Nuxt 4** - 充分利用最新的 Nuxt 框架,实现卓越性能
19
38
  - 🎨 **采用 Nuxt UI** - 集成全面的 UI 组件库,开箱即用
20
- - 📝 **MDC 语法增强** - 支持 Markdown 与 Vue 组件的无缝集成,实现动态内容
21
- - 🧩 **组件文档自动生成** - 自动生成 Props、Slots、Emits 文档及交互式示例
22
- - 📦 **Git 提交历史集成** - 通过 CommitChangelog 组件自动展示文件的提交历史记录
23
- - 📚 **智能侧边栏导航** - 根据内容结构自动生成导航
24
- - 🔍 **全文搜索** - 内置强大的全文搜索功能
39
+ - 📝 **MDC 语法增强** - 支持 Markdown 与 Vue 组件的无缝集成
40
+ - 🔍 **全文搜索** - 基于 Nuxt Content 的 `ContentSearch` 组件,支持键盘快捷键(⌘K)
25
41
  - 🌙 **暗黑模式** - 支持亮色/暗色主题切换
26
42
  - 📱 **响应式设计** - 移动优先的响应式布局
27
43
  - 🚀 **SEO 优化** - 内置 SEO 优化功能
28
44
  - 🎯 **TypeScript 支持** - 完整的 TypeScript 类型支持
29
- - 🤖 **AI 助手优化** - 为 LLM 优化,提供更好的 AI 辅助文档体验
30
45
 
31
46
  ## 🚀 快速开始
32
47
 
@@ -80,10 +95,19 @@ pnpm add @movk/nuxt-docs better-sqlite3
80
95
  export default defineNuxtConfig({
81
96
  extends: ['@movk/nuxt-docs'],
82
97
  css: ['~/assets/css/main.css'],
98
+ aiChat: {
99
+ model: 'mistral/devstral-2',
100
+ models: ['mistral/devstral-2', 'openrouter/qwen/qwen3-4b:free']
101
+ },
102
+ mcp: {
103
+ name: 'My Docs',
104
+ browserRedirect: '/docs'
105
+ },
83
106
  llms: {
84
- domain: 'https://docs.mhaibaraai.cn',
85
- title: 'Movk Nuxt Docs',
86
- description: '一款优雅的 Nuxt 文档主题'
107
+ domain: 'https://your-domain.com',
108
+ title: 'My Docs',
109
+ description: '基于 Movk Nuxt Docs 构建的智能文档站点',
110
+ notes: ['Nuxt 4', '文档主题', 'TypeScript']
87
111
  }
88
112
  })
89
113
  ```
@@ -129,7 +153,7 @@ movk-nuxt-docs/
129
153
 
130
154
  ### 基础 Markdown
131
155
 
132
- ```md
156
+ ```md [md]
133
157
  ---
134
158
  title: 页面标题
135
159
  description: 页面描述
@@ -147,7 +171,7 @@ description: 页面描述
147
171
 
148
172
  ### MDC 语法
149
173
 
150
- ```md
174
+ ```md [md]
151
175
  ::card
152
176
  ---
153
177
  title: 卡片标题
@@ -159,14 +183,6 @@ icon: i-lucide-rocket
159
183
 
160
184
  了解更多关于 MDC 语法,请查看 [Nuxt Content 文档](https://content.nuxt.com/docs/files/markdown#mdc-syntax)。
161
185
 
162
- ### 其他工具
163
-
164
- - **Google Analytics** - [@nuxtjs/google-analytics](https://google-analytics.nuxtjs.org/)
165
- - **Plausible** - [vue-plausible](https://github.com/moritzsternemann/vue-plausible)
166
- - **Umami** - [nuxt-umami](https://github.com/ijkml/nuxt-umami)
167
-
168
- 按照各工具的 Nuxt 集成文档在 `plugins` 目录中创建插件即可。
169
-
170
186
  ## 🛠️ 开发
171
187
 
172
188
  ### 本地开发
@@ -206,13 +222,27 @@ pnpm release
206
222
 
207
223
  本项目基于以下优秀的开源项目构建:
208
224
 
225
+ ### 核心框架
226
+
209
227
  - [Nuxt 4](https://nuxt.com/) - Web 框架
210
228
  - [Nuxt Content](https://content.nuxt.com/) - 基于文件的 CMS
211
229
  - [Nuxt UI](https://ui.nuxt.com/) - UI 组件库
212
- - [Nuxt Image](https://image.nuxt.com/) - 图片优化
213
230
  - [Tailwind CSS 4](https://tailwindcss.com/) - CSS 框架
231
+
232
+ ### AI 集成
233
+
234
+ - [Vercel AI SDK](https://sdk.vercel.ai/) - AI 集成框架
235
+ - [Nuxt LLMs](https://github.com/nuxt-content/nuxt-llms) - LLM 优化
236
+ - [@nuxtjs/mcp-toolkit](https://github.com/nuxt-modules/mcp-toolkit) - MCP Server 支持
237
+ - [Shiki](https://shiki.style/) - 代码语法高亮
238
+ - [Shiki Stream](https://github.com/antfu/shiki-stream) - 流式代码高亮
239
+
240
+ ### 功能增强
241
+
242
+ - [Nuxt Component Meta](https://github.com/nuxt-content/nuxt-component-meta) - 组件元数据提取
243
+ - [Nuxt Image](https://image.nuxt.com/) - 图片优化
214
244
  - [Nuxt SEO](https://nuxtseo.com/) - SEO 优化
215
- - [Nuxt LLMs](https://github.com/nuxt/llms) - AI 助手优化
245
+ - [Octokit](https://github.com/octokit/rest.js) - GitHub API 集成
216
246
 
217
247
  ## 📖 文档
218
248
 
@@ -0,0 +1,13 @@
1
+ <script lang="ts" setup>
2
+ import type { ContentNavigationItem } from '@nuxt/content'
3
+
4
+ const route = useRoute()
5
+
6
+ const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
7
+
8
+ const { navigationByCategory } = useNavigation(navigation!)
9
+ </script>
10
+
11
+ <template>
12
+ <UContentNavigation :key="route.path" :navigation="navigationByCategory" highlight />
13
+ </template>
@@ -0,0 +1,6 @@
1
+ <script lang="ts" setup>
2
+ </script>
3
+
4
+ <template>
5
+ <div />
6
+ </template>
@@ -0,0 +1,29 @@
1
+ <script setup lang="ts">
2
+ const { aiChat } = useRuntimeConfig().public
3
+ const route = useRoute()
4
+
5
+ const pageUrl = route.path
6
+ const { open } = useAIChat()
7
+ const { faqQuestions } = useFaq()
8
+ </script>
9
+
10
+ <template>
11
+ <div v-if="aiChat.enable">
12
+ <UButton
13
+ icon="i-lucide-brain"
14
+ target="_blank"
15
+ label="用 AI 解释此页面"
16
+ size="sm"
17
+ variant="ghost"
18
+ color="neutral"
19
+ @click="open(`解释此页面 ${pageUrl}`, true)"
20
+ />
21
+ <AiChatSlideover :faq-questions="faqQuestions" />
22
+
23
+ <Teleport to="body">
24
+ <ClientOnly>
25
+ <LazyAiChatFloatingInput />
26
+ </ClientOnly>
27
+ </Teleport>
28
+ </div>
29
+ </template>
@@ -5,14 +5,14 @@
5
5
  */
6
6
  import { computed } from 'vue'
7
7
 
8
- const props = withDefaults(defineProps<{ title?: string, description?: string, headline?: string }>(), {
9
- title: 'title',
10
- description: 'description',
11
- headline: 'headline'
12
- })
8
+ const {
9
+ title = 'title',
10
+ description = 'description',
11
+ headline = 'headline'
12
+ } = defineProps<{ title?: string, description?: string, headline?: string }>()
13
13
 
14
- const title = computed(() => (props.title || '').slice(0, 60))
15
- const description = computed(() => (props.description || '').slice(0, 200))
14
+ const computedTitle = computed(() => (title || '').slice(0, 60))
15
+ const computedDescription = computed(() => (description || '').slice(0, 200))
16
16
  </script>
17
17
 
18
18
  <template>
@@ -26,7 +26,10 @@ const description = computed(() => (props.description || '').slice(0, 200))
26
26
  xmlns="http://www.w3.org/2000/svg"
27
27
  >
28
28
  <g filter="url(#filter0_f_199_94966)">
29
- <path d="M628.5 -578L639.334 -94.4223L806.598 -548.281L659.827 -87.387L965.396 -462.344L676.925 -74.0787L1087.69 -329.501L688.776 -55.9396L1160.22 -164.149L694.095 -34.9354L1175.13 15.7948L692.306 -13.3422L1130.8 190.83L683.602 6.50012L1032.04 341.989L668.927 22.4412L889.557 452.891L649.872 32.7537L718.78 511.519L628.5 36.32L538.22 511.519L607.128 32.7537L367.443 452.891L588.073 22.4412L224.955 341.989L573.398 6.50012L126.198 190.83L564.694 -13.3422L81.8734 15.7948L562.905 -34.9354L96.7839 -164.149L568.224 -55.9396L169.314 -329.501L580.075 -74.0787L291.604 -462.344L597.173 -87.387L450.402 -548.281L617.666 -94.4223L628.5 -578Z" fill="#00DC82" />
29
+ <path
30
+ d="M628.5 -578L639.334 -94.4223L806.598 -548.281L659.827 -87.387L965.396 -462.344L676.925 -74.0787L1087.69 -329.501L688.776 -55.9396L1160.22 -164.149L694.095 -34.9354L1175.13 15.7948L692.306 -13.3422L1130.8 190.83L683.602 6.50012L1032.04 341.989L668.927 22.4412L889.557 452.891L649.872 32.7537L718.78 511.519L628.5 36.32L538.22 511.519L607.128 32.7537L367.443 452.891L588.073 22.4412L224.955 341.989L573.398 6.50012L126.198 190.83L564.694 -13.3422L81.8734 15.7948L562.905 -34.9354L96.7839 -164.149L568.224 -55.9396L169.314 -329.501L580.075 -74.0787L291.604 -462.344L597.173 -87.387L450.402 -548.281L617.666 -94.4223L628.5 -578Z"
31
+ fill="#00DC82"
32
+ />
30
33
  </g>
31
34
  <defs>
32
35
  <filter
@@ -54,11 +57,19 @@ const description = computed(() => (props.description || '').slice(0, 200))
54
57
  <p v-if="headline" class="uppercase text-[24px] text-[#00DC82] mb-4 font-semibold">
55
58
  {{ headline }}
56
59
  </p>
57
- <h1 v-if="title" class="w-[600px] m-0 text-[75px] font-semibold mb-4 text-white" style="display: block; line-clamp: 2; text-overflow: ellipsis;">
58
- {{ title }}
60
+ <h1
61
+ v-if="title"
62
+ class="w-[600px] m-0 text-[75px] font-semibold mb-4 text-white"
63
+ style="display: block; line-clamp: 2; text-overflow: ellipsis;"
64
+ >
65
+ {{ computedTitle }}
59
66
  </h1>
60
- <p v-if="description" class="text-[32px] text-[#E4E4E7] leading-tight" style="display: block; line-clamp: 3; text-overflow: ellipsis;">
61
- {{ description }}
67
+ <p
68
+ v-if="description"
69
+ class="text-[32px] text-[#E4E4E7] leading-tight"
70
+ style="display: block; line-clamp: 3; text-overflow: ellipsis;"
71
+ >
72
+ {{ computedDescription }}
62
73
  </p>
63
74
  </div>
64
75
  <svg
@@ -69,8 +80,15 @@ const description = computed(() => (props.description || '').slice(0, 200))
69
80
  fill="none"
70
81
  xmlns="http://www.w3.org/2000/svg"
71
82
  >
72
- <path d="M86.6286 103.106C88.2099 94.7477 94.7477 88.2099 103.106 86.6286L104.427 86.3788C146.343 78.4485 189.386 78.5576 231.262 86.7002L232.272 86.8967C239.615 88.3244 245.4 93.99 246.981 101.301L247.277 102.671C256.565 145.63 256.438 190.092 246.903 232.997C245.361 239.939 239.939 245.361 232.997 246.903C190.092 256.438 145.63 256.565 102.671 247.277L101.301 246.981C93.99 245.4 88.3244 239.615 86.8967 232.272L86.7002 231.262C78.5576 189.386 78.4485 146.343 86.3788 104.426L86.6286 103.106Z" fill="url(#paint0_linear_199_94959)" />
73
- <path d="M86.6286 103.106C88.2099 94.7477 94.7477 88.2099 103.106 86.6286L104.427 86.3788C146.343 78.4485 189.386 78.5576 231.262 86.7002L232.272 86.8967C239.615 88.3244 245.4 93.99 246.981 101.301L247.277 102.671C256.565 145.63 256.438 190.092 246.903 232.997C245.361 239.939 239.939 245.361 232.997 246.903C190.092 256.438 145.63 256.565 102.671 247.277L101.301 246.981C93.99 245.4 88.3244 239.615 86.8967 232.272L86.7002 231.262C78.5576 189.386 78.4485 146.343 86.3788 104.426L86.6286 103.106Z" fill="url(#paint1_radial_199_94959)" fill-opacity="0.06" />
83
+ <path
84
+ d="M86.6286 103.106C88.2099 94.7477 94.7477 88.2099 103.106 86.6286L104.427 86.3788C146.343 78.4485 189.386 78.5576 231.262 86.7002L232.272 86.8967C239.615 88.3244 245.4 93.99 246.981 101.301L247.277 102.671C256.565 145.63 256.438 190.092 246.903 232.997C245.361 239.939 239.939 245.361 232.997 246.903C190.092 256.438 145.63 256.565 102.671 247.277L101.301 246.981C93.99 245.4 88.3244 239.615 86.8967 232.272L86.7002 231.262C78.5576 189.386 78.4485 146.343 86.3788 104.426L86.6286 103.106Z"
85
+ fill="url(#paint0_linear_199_94959)"
86
+ />
87
+ <path
88
+ d="M86.6286 103.106C88.2099 94.7477 94.7477 88.2099 103.106 86.6286L104.427 86.3788C146.343 78.4485 189.386 78.5576 231.262 86.7002L232.272 86.8967C239.615 88.3244 245.4 93.99 246.981 101.301L247.277 102.671C256.565 145.63 256.438 190.092 246.903 232.997C245.361 239.939 239.939 245.361 232.997 246.903C190.092 256.438 145.63 256.565 102.671 247.277L101.301 246.981C93.99 245.4 88.3244 239.615 86.8967 232.272L86.7002 231.262C78.5576 189.386 78.4485 146.343 86.3788 104.426L86.6286 103.106Z"
89
+ fill="url(#paint1_radial_199_94959)"
90
+ fill-opacity="0.06"
91
+ />
74
92
  <path
75
93
  d="M103.028 86.2151C94.4994 87.8286 87.8286 94.4994 86.2151 103.028L85.9653 104.348C78.0252 146.318 78.1344 189.414 86.2872 231.342L86.4836 232.353C87.9434 239.86 93.7366 245.776 101.212 247.392L102.582 247.688C145.601 256.989 190.124 256.862 233.089 247.314C240.19 245.736 245.736 240.19 247.314 233.089C256.862 190.124 256.989 145.601 247.688 102.582L247.392 101.212C245.776 93.7366 239.86 87.9434 232.353 86.4836L231.342 86.2872C189.414 78.1344 146.318 78.0252 104.348 85.9653L103.028 86.2151Z"
76
94
  stroke="url(#paint2_linear_199_94959)"
@@ -95,7 +113,10 @@ const description = computed(() => (props.description || '').slice(0, 200))
95
113
  stroke-opacity="0.2"
96
114
  stroke-width="2.125"
97
115
  />
98
- <path d="M174.667 190.325H203.105C204.009 190.325 204.896 190.084 205.678 189.626C206.461 189.168 207.11 188.509 207.561 187.715C208.013 186.921 208.25 186.021 208.25 185.105C208.25 184.188 208.011 183.288 207.559 182.495L188.461 148.938C188.009 148.145 187.36 147.486 186.578 147.028C185.796 146.57 184.909 146.328 184.006 146.328C183.103 146.328 182.215 146.57 181.433 147.028C180.651 147.486 180.002 148.145 179.551 148.938L174.667 157.524L165.119 140.734C164.668 139.941 164.018 139.282 163.236 138.824C162.453 138.366 161.566 138.125 160.663 138.125C159.76 138.125 158.872 138.366 158.09 138.824C157.308 139.282 156.658 139.941 156.206 140.734L132.441 182.495C131.989 183.288 131.75 184.188 131.75 185.105C131.75 186.021 131.987 186.921 132.439 187.715C132.89 188.509 133.539 189.168 134.322 189.626C135.104 190.084 135.991 190.325 136.895 190.325H154.746C161.819 190.325 167.035 187.173 170.624 181.025L179.337 165.717L184.004 157.524L198.011 182.133H179.337L174.667 190.325ZM154.455 182.124L141.997 182.121L160.671 149.312L169.989 165.717L163.75 176.681C161.367 180.671 158.659 182.124 154.455 182.124Z" fill="#00DC82" />
116
+ <path
117
+ d="M174.667 190.325H203.105C204.009 190.325 204.896 190.084 205.678 189.626C206.461 189.168 207.11 188.509 207.561 187.715C208.013 186.921 208.25 186.021 208.25 185.105C208.25 184.188 208.011 183.288 207.559 182.495L188.461 148.938C188.009 148.145 187.36 147.486 186.578 147.028C185.796 146.57 184.909 146.328 184.006 146.328C183.103 146.328 182.215 146.57 181.433 147.028C180.651 147.486 180.002 148.145 179.551 148.938L174.667 157.524L165.119 140.734C164.668 139.941 164.018 139.282 163.236 138.824C162.453 138.366 161.566 138.125 160.663 138.125C159.76 138.125 158.872 138.366 158.09 138.824C157.308 139.282 156.658 139.941 156.206 140.734L132.441 182.495C131.989 183.288 131.75 184.188 131.75 185.105C131.75 186.021 131.987 186.921 132.439 187.715C132.89 188.509 133.539 189.168 134.322 189.626C135.104 190.084 135.991 190.325 136.895 190.325H154.746C161.819 190.325 167.035 187.173 170.624 181.025L179.337 165.717L184.004 157.524L198.011 182.133H179.337L174.667 190.325ZM154.455 182.124L141.997 182.121L160.671 149.312L169.989 165.717L163.75 176.681C161.367 180.671 158.659 182.124 154.455 182.124Z"
118
+ fill="#00DC82"
119
+ />
99
120
  <path
100
121
  d="M176.761 189.109H203.105H203.106C203.792 189.109 204.467 188.926 205.064 188.576C205.66 188.227 206.158 187.723 206.504 187.113L207.561 187.715L206.504 187.113C206.851 186.504 207.034 185.811 207.034 185.105C207.033 184.399 206.85 183.707 206.502 183.098L206.502 183.097L187.404 149.54L187.404 149.54C187.057 148.93 186.56 148.427 185.963 148.077C185.367 147.728 184.692 147.545 184.006 147.545C183.32 147.545 182.645 147.728 182.048 148.077C181.452 148.427 180.954 148.93 180.608 149.539C180.608 149.539 180.608 149.54 180.608 149.54L175.725 158.126L174.667 159.985L173.61 158.125L164.062 141.336L176.761 189.109ZM176.761 189.109L180.044 183.349H198.011H200.103L199.069 181.531L185.061 156.922L184.005 155.066L182.947 156.922L178.28 165.115L178.28 165.115L169.57 180.417C166.187 186.208 161.362 189.109 154.746 189.109H136.895H136.894C136.208 189.109 135.533 188.926 134.936 188.576C134.34 188.227 133.842 187.723 133.496 187.113L132.44 187.714L133.496 187.113C133.149 186.504 132.966 185.811 132.966 185.105C132.967 184.399 133.15 183.707 133.498 183.098L133.498 183.097L157.263 141.336C157.263 141.336 157.263 141.336 157.263 141.336C157.61 140.727 158.108 140.223 158.705 139.874C159.301 139.525 159.976 139.341 160.663 139.341C161.349 139.341 162.025 139.525 162.621 139.874L163.236 138.824L162.621 139.874C163.218 140.223 163.715 140.727 164.062 141.336L176.761 189.109ZM154.454 183.34H154.455C156.699 183.34 158.653 182.952 160.391 181.953C162.126 180.957 163.533 179.417 164.794 177.305L164.801 177.294L164.807 177.283L171.046 166.318L171.388 165.717L171.047 165.116L161.729 148.711L160.672 146.851L159.614 148.71L140.94 181.52L139.905 183.337L141.997 183.338L154.454 183.34Z"
101
122
  stroke="url(#paint6_linear_199_94959)"
@@ -103,7 +124,10 @@ const description = computed(() => (props.description || '').slice(0, 200))
103
124
  stroke-width="2.43271"
104
125
  />
105
126
  <g filter="url(#filter0_f_199_94959)">
106
- <path d="M174.667 190.325H203.105C204.009 190.325 204.896 190.084 205.678 189.626C206.461 189.168 207.11 188.509 207.561 187.715C208.013 186.921 208.25 186.021 208.25 185.105C208.25 184.188 208.011 183.288 207.559 182.495L188.461 148.938C188.009 148.145 187.36 147.486 186.578 147.028C185.796 146.57 184.909 146.328 184.006 146.328C183.103 146.328 182.215 146.57 181.433 147.028C180.651 147.486 180.002 148.145 179.551 148.938L174.667 157.524L165.119 140.734C164.668 139.941 164.018 139.282 163.236 138.824C162.453 138.366 161.566 138.125 160.663 138.125C159.76 138.125 158.872 138.366 158.09 138.824C157.308 139.282 156.658 139.941 156.206 140.734L132.441 182.495C131.989 183.288 131.75 184.188 131.75 185.105C131.75 186.021 131.987 186.921 132.439 187.715C132.89 188.509 133.539 189.168 134.322 189.626C135.104 190.084 135.991 190.325 136.895 190.325H154.746C161.819 190.325 167.035 187.173 170.624 181.025L179.337 165.717L184.004 157.524L198.011 182.133H179.337L174.667 190.325ZM154.455 182.124L141.997 182.121L160.671 149.312L169.989 165.717L163.75 176.681C161.367 180.671 158.659 182.124 154.455 182.124Z" fill="#00DC82" />
127
+ <path
128
+ d="M174.667 190.325H203.105C204.009 190.325 204.896 190.084 205.678 189.626C206.461 189.168 207.11 188.509 207.561 187.715C208.013 186.921 208.25 186.021 208.25 185.105C208.25 184.188 208.011 183.288 207.559 182.495L188.461 148.938C188.009 148.145 187.36 147.486 186.578 147.028C185.796 146.57 184.909 146.328 184.006 146.328C183.103 146.328 182.215 146.57 181.433 147.028C180.651 147.486 180.002 148.145 179.551 148.938L174.667 157.524L165.119 140.734C164.668 139.941 164.018 139.282 163.236 138.824C162.453 138.366 161.566 138.125 160.663 138.125C159.76 138.125 158.872 138.366 158.09 138.824C157.308 139.282 156.658 139.941 156.206 140.734L132.441 182.495C131.989 183.288 131.75 184.188 131.75 185.105C131.75 186.021 131.987 186.921 132.439 187.715C132.89 188.509 133.539 189.168 134.322 189.626C135.104 190.084 135.991 190.325 136.895 190.325H154.746C161.819 190.325 167.035 187.173 170.624 181.025L179.337 165.717L184.004 157.524L198.011 182.133H179.337L174.667 190.325ZM154.455 182.124L141.997 182.121L160.671 149.312L169.989 165.717L163.75 176.681C161.367 180.671 158.659 182.124 154.455 182.124Z"
129
+ fill="#00DC82"
130
+ />
107
131
  <path
108
132
  d="M176.761 189.109H203.105H203.106C203.792 189.109 204.467 188.926 205.064 188.576C205.66 188.227 206.158 187.723 206.504 187.113L207.561 187.715L206.504 187.113C206.851 186.504 207.034 185.811 207.034 185.105C207.033 184.399 206.85 183.707 206.502 183.098L206.502 183.097L187.404 149.54L187.404 149.54C187.057 148.93 186.56 148.427 185.963 148.077C185.367 147.728 184.692 147.545 184.006 147.545C183.32 147.545 182.645 147.728 182.048 148.077C181.452 148.427 180.954 148.93 180.608 149.539C180.608 149.539 180.608 149.54 180.608 149.54L175.725 158.126L174.667 159.985L173.61 158.125L164.062 141.336L176.761 189.109ZM176.761 189.109L180.044 183.349H198.011H200.103L199.069 181.531L185.061 156.922L184.005 155.066L182.947 156.922L178.28 165.115L178.28 165.115L169.57 180.417C166.187 186.208 161.362 189.109 154.746 189.109H136.895H136.894C136.208 189.109 135.533 188.926 134.936 188.576C134.34 188.227 133.842 187.723 133.496 187.113L132.44 187.714L133.496 187.113C133.149 186.504 132.966 185.811 132.966 185.105C132.967 184.399 133.15 183.707 133.498 183.098L133.498 183.097L157.263 141.336C157.263 141.336 157.263 141.336 157.263 141.336C157.61 140.727 158.108 140.223 158.705 139.874C159.301 139.525 159.976 139.341 160.663 139.341C161.349 139.341 162.025 139.525 162.621 139.874L163.236 138.824L162.621 139.874C163.218 140.223 163.715 140.727 164.062 141.336L176.761 189.109ZM154.454 183.34H154.455C156.699 183.34 158.653 182.952 160.391 181.953C162.126 180.957 163.533 179.417 164.794 177.305L164.801 177.294L164.807 177.283L171.046 166.318L171.388 165.717L171.047 165.116L161.729 148.711L160.672 146.851L159.614 148.71L140.94 181.52L139.905 183.337L141.997 183.338L154.454 183.34Z"
109
133
  stroke="url(#paint7_linear_199_94959)"
@@ -3,17 +3,19 @@ const route = useRoute()
3
3
  const toast = useToast()
4
4
  const { copy, copied } = useClipboard()
5
5
  const site = useSiteConfig()
6
- const { vercelAnalytics } = useAppConfig()
6
+ const { vercelAnalytics, ui } = useAppConfig()
7
7
  const { track } = useAnalytics()
8
8
 
9
+ const appBaseURL = useRuntimeConfig().app?.baseURL || '/'
10
+
9
11
  const mdPath = computed(() => `${site.url}/raw${route.path}.md`)
10
12
 
11
- const items = [
13
+ const items = [[
12
14
  {
13
15
  label: 'Copy Markdown link',
14
16
  icon: 'i-lucide-link',
15
17
  onSelect() {
16
- if (vercelAnalytics?.debug) track ('Page Action', { action: 'Copy Markdown Link' })
18
+ if (vercelAnalytics?.debug) track('Page Action', { action: 'Copy Markdown Link' })
17
19
  copy(mdPath.value)
18
20
  toast.add({
19
21
  title: 'Copied to clipboard',
@@ -23,7 +25,7 @@ const items = [
23
25
  },
24
26
  {
25
27
  label: 'View as Markdown',
26
- icon: 'i-simple-icons:markdown',
28
+ icon: 'i-simple-icons-markdown',
27
29
  target: '_blank',
28
30
  to: `/raw${route.path}.md`,
29
31
  onSelect() {
@@ -32,7 +34,7 @@ const items = [
32
34
  },
33
35
  {
34
36
  label: 'Open in ChatGPT',
35
- icon: 'i-simple-icons:openai',
37
+ icon: 'i-simple-icons-openai',
36
38
  target: '_blank',
37
39
  to: `https://chatgpt.com/?hints=search&q=${encodeURIComponent(`Read ${mdPath.value} so I can ask questions about it.`)}`,
38
40
  onSelect() {
@@ -41,14 +43,38 @@ const items = [
41
43
  },
42
44
  {
43
45
  label: 'Open in Claude',
44
- icon: 'i-simple-icons:anthropic',
46
+ icon: 'i-simple-icons-anthropic',
45
47
  target: '_blank',
46
48
  to: `https://claude.ai/new?q=${encodeURIComponent(`Read ${mdPath.value} so I can ask questions about it.`)}`,
47
49
  onSelect() {
48
50
  if (vercelAnalytics?.debug) track('Page Action', { action: 'Open in Claude' })
49
51
  }
50
52
  }
51
- ]
53
+ ], [
54
+ {
55
+ label: 'Copy MCP Server URL',
56
+ icon: 'i-lucide-cpu',
57
+ onSelect() {
58
+ copy(`${window?.location?.origin}${appBaseURL}mcp`)
59
+ toast.add({
60
+ title: 'Copied to clipboard',
61
+ icon: 'i-lucide-circle-check'
62
+ })
63
+ }
64
+ },
65
+ {
66
+ label: 'Add MCP Server to VSCode',
67
+ icon: 'i-simple-icons-visualstudiocode',
68
+ target: '_blank',
69
+ to: `/mcp/deeplink?ide=vscode`
70
+ },
71
+ {
72
+ label: 'Add MCP Server to Cursor',
73
+ icon: 'i-simple-icons-cursor',
74
+ target: '_blank',
75
+ to: `/mcp/deeplink`
76
+ }
77
+ ]]
52
78
 
53
79
  async function copyPage() {
54
80
  if (vercelAnalytics?.debug) track('Page Action', { action: 'Copy Page Content' })
@@ -60,7 +86,7 @@ async function copyPage() {
60
86
  <UFieldGroup size="sm">
61
87
  <UButton
62
88
  label="Copy page"
63
- :icon="copied ? 'i-lucide-copy-check' : 'i-lucide-copy'"
89
+ :icon="copied ? ui.icons.copyCheck : ui.icons.copy"
64
90
  color="neutral"
65
91
  variant="outline"
66
92
  :ui="{
@@ -80,7 +106,7 @@ async function copyPage() {
80
106
  content: 'w-48'
81
107
  }"
82
108
  >
83
- <UButton icon="i-lucide-chevron-down" color="neutral" variant="outline" />
109
+ <UButton :icon="ui.icons.chevronDown" color="neutral" variant="outline" />
84
110
  </UDropdownMenu>
85
111
  </UFieldGroup>
86
112
  </template>
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { camelCase, upperFirst } from '@movk/core'
2
+ import { camelCase, kebabCase, upperFirst } from '@movk/core'
3
3
 
4
4
  interface Commit {
5
5
  sha: string
@@ -8,27 +8,36 @@ interface Commit {
8
8
 
9
9
  const props = defineProps<{
10
10
  /**
11
- * The path to the file in the repository.
11
+ * 仓库中的文件路径
12
12
  * @defaultValue 'src'
13
13
  */
14
14
  commitPath?: string
15
15
  /**
16
- * The prefix for the file path.
16
+ * 文件路径的前缀
17
17
  */
18
18
  prefix?: string
19
19
  /**
20
- * The file extension.
20
+ * 文件扩展名
21
21
  * @defaultValue 'vue'
22
22
  */
23
23
  suffix?: string
24
24
  /**
25
- * The name of the component or file to get the changelog for.
25
+ * 要获取更新日志的组件或文件名
26
26
  */
27
27
  name?: string
28
28
  /**
29
- * The author to filter commits by.
29
+ * 按作者筛选提交
30
30
  */
31
31
  author?: string
32
+ /**
33
+ * 文件名的命名格式
34
+ * - 'auto': Vue 文件使用 PascalCase,其他使用 camelCase(默认)
35
+ * - 'kebab': 保持 kebab-case(如 use-user.ts)
36
+ * - 'camel': 转换为 camelCase(如 useUser.ts)
37
+ * - 'pascal': 转换为 PascalCase(如 UseUser.ts)
38
+ * @defaultValue 'auto'
39
+ */
40
+ casing?: 'auto' | 'kebab' | 'camel' | 'pascal'
32
41
  }>()
33
42
 
34
43
  const SHA_SHORT_LENGTH = 5
@@ -46,11 +55,26 @@ const filePath = computed(() => {
46
55
  const fileExtension = props.suffix ?? (github && typeof github === 'object' ? github.suffix : 'vue')
47
56
  const fileName = props.name ?? routeName.value
48
57
 
49
- const camelName = fileExtension === 'vue'
50
- ? upperFirst(camelCase(fileName))
51
- : camelCase(fileName)
58
+ // 根据 casing 参数转换文件名
59
+ const transformedName = (() => {
60
+ const casing = props.casing ?? (github && typeof github === 'object' ? github.casing : undefined) ?? 'auto'
61
+
62
+ switch (casing) {
63
+ case 'kebab':
64
+ return kebabCase(fileName)
65
+ case 'camel':
66
+ return camelCase(fileName)
67
+ case 'pascal':
68
+ return upperFirst(camelCase(fileName))
69
+ case 'auto':
70
+ default:
71
+ return fileExtension === 'vue'
72
+ ? upperFirst(camelCase(fileName))
73
+ : camelCase(fileName)
74
+ }
75
+ })()
52
76
 
53
- return `${basePath}/${filePrefix}${camelName}.${fileExtension}`
77
+ return `${basePath}/${filePrefix}${transformedName}.${fileExtension}`
54
78
  })
55
79
 
56
80
  const { data: commits } = await useLazyFetch<Commit[]>('/api/github/commits', {
@@ -3,8 +3,8 @@ import { camelCase } from 'scule'
3
3
 
4
4
  const props = defineProps<{
5
5
  /**
6
- * The slug of the component to fetch emits for.
7
- * @defaultValue route path's last segment
6
+ * 获取组件事件的 slug 标识
7
+ * @defaultValue 路由路径的最后一段
8
8
  */
9
9
  slug?: string
10
10
  }>()
@@ -9,23 +9,23 @@ const { preview = true, source = true, prettier = false, ...props } = defineProp
9
9
  name: string
10
10
  class?: any
11
11
  /**
12
- * Whether to render the component in an iframe
12
+ * 是否在 iframe 中渲染组件
13
13
  * @defaultValue false
14
14
  */
15
15
  iframe?: boolean | { [key: string]: any }
16
16
  /**
17
- * Whether to display the component in a mobile-sized iframe viewport
17
+ * 是否在移动端尺寸的 iframe 视口中显示组件
18
18
  * @defaultValue false
19
19
  */
20
20
  iframeMobile?: boolean
21
21
  props?: { [key: string]: any }
22
22
  /**
23
- * Whether to format the code with Prettier
23
+ * 是否使用 Prettier 格式化代码
24
24
  * @defaultValue false
25
25
  */
26
26
  prettier?: boolean
27
27
  /**
28
- * Whether to collapse the code block
28
+ * 是否折叠代码块
29
29
  * @defaultValue false
30
30
  */
31
31
  collapse?: boolean | {
@@ -36,18 +36,18 @@ const { preview = true, source = true, prettier = false, ...props } = defineProp
36
36
  open?: boolean
37
37
  }
38
38
  /**
39
- * Whether to show the preview
40
- * When `false`, the filename will be shown instead
39
+ * 是否显示预览
40
+ * 当设置为 `false` 时,将显示文件名
41
41
  * @defaultValue true
42
42
  */
43
43
  preview?: boolean
44
44
  /**
45
- * Whether to show the source code
45
+ * 是否显示源代码
46
46
  * @defaultValue true
47
47
  */
48
48
  source?: boolean
49
49
  /**
50
- * A list of variable props to link to the component.
50
+ * 链接到组件的可变属性列表
51
51
  */
52
52
  options?: Array<{
53
53
  alias?: string
@@ -58,11 +58,11 @@ const { preview = true, source = true, prettier = false, ...props } = defineProp
58
58
  multiple?: boolean
59
59
  }>
60
60
  /**
61
- * A list of line numbers to highlight in the code block
61
+ * 代码块中需要高亮的行号列表
62
62
  */
63
63
  highlights?: number[]
64
64
  /**
65
- * Whether to add overflow-hidden to wrapper
65
+ * 是否在包装器上添加 overflow-hidden
66
66
  */
67
67
  overflowHidden?: boolean
68
68
  }>()
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { ComponentMeta } from 'vue-component-meta'
3
3
  import { camelCase, kebabCase, upperFirst } from 'scule'
4
+ import { decodeUnicodeEscapes } from '../../utils/unicode'
4
5
 
5
6
  const { ignore = [
6
7
  'activeClass',
@@ -30,12 +31,12 @@ const { ignore = [
30
31
  'formtarget'
31
32
  ], slug, prose } = defineProps<{
32
33
  /**
33
- * The slug of the component to fetch props for.
34
- * @defaultValue route path's last segment
34
+ * 获取组件属性的 slug 标识
35
+ * @defaultValue 路由路径的最后一段
35
36
  */
36
37
  slug?: string
37
38
  /**
38
- * An array of prop names to ignore.
39
+ * 需要忽略的属性名数组
39
40
  */
40
41
  ignore?: string[]
41
42
  prose?: boolean
@@ -56,6 +57,7 @@ const metaProps: ComputedRef<ComponentMeta['props']> = computed(() => {
56
57
  }).map((prop) => {
57
58
  if (prop.default) {
58
59
  prop.default = prop.default.replace(' as never', '').replace(/^"(.*)"$/, '\'$1\'')
60
+ prop.default = decodeUnicodeEscapes(prop.default)
59
61
  } else {
60
62
  const tag = prop.tags?.find(tag => tag.name === 'defaultValue')?.text
61
63
  if (tag) {
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { kebabCase } from 'scule'
3
3
  import type { PropertyMeta } from 'vue-component-meta'
4
+ import { decodeUnicodeEscapes } from '../../utils/unicode'
4
5
 
5
6
  const props = defineProps<{
6
7
  prop: PropertyMeta
@@ -24,7 +25,10 @@ function getSchemaProps(schema: PropertyMeta['schema']): any {
24
25
  const schemaProps = computed(() => {
25
26
  const propsObject = getSchemaProps(props.prop.schema).reduce((acc: any, prop: any) => {
26
27
  if (!acc[prop.name]) {
27
- const defaultValue = prop.default ?? prop.tags?.find((tag: any) => tag.name === 'defaultValue')?.text
28
+ let defaultValue = prop.default ?? prop.tags?.find((tag: any) => tag.name === 'defaultValue')?.text
29
+ if (defaultValue && typeof defaultValue === 'string') {
30
+ defaultValue = decodeUnicodeEscapes(defaultValue)
31
+ }
28
32
  let description = prop.description
29
33
  if (defaultValue) {
30
34
  description = description ? `${description} Defaults to \`${defaultValue}\`{lang="ts-type"}.` : `Defaults to \`${defaultValue}\`{lang="ts-type"}.`
@@ -3,8 +3,8 @@ import { camelCase, kebabCase } from 'scule'
3
3
 
4
4
  const props = defineProps<{
5
5
  /**
6
- * The slug of the component to fetch slots for.
7
- * @defaultValue route path's last segment
6
+ * 获取组件插槽的 slug 标识
7
+ * @defaultValue 路由路径的最后一段
8
8
  */
9
9
  slug?: string
10
10
  }>()
@@ -24,7 +24,7 @@ const type = computed(() => {
24
24
  const ast = ref<any>(null)
25
25
 
26
26
  onMounted(async () => {
27
- ast.value = await parseMarkdown(`\`${type.value}\`{lang="ts-type"}`)
27
+ ast.value = await parseMarkdown(`\`\` ${type.value} \`\`{lang="ts-type"}`)
28
28
  })
29
29
  </script>
30
30