@movk/nuxt-docs 1.7.3 → 1.7.5
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/app/error.vue +2 -2
- package/app/pages/docs/[...slug].vue +15 -0
- package/app/workers/prettier.js +6 -6
- package/modules/ai-chat/index.ts +1 -1
- package/modules/ai-chat/runtime/server/api/ai-chat.ts +19 -29
- package/modules/ai-chat/runtime/server/utils/docs_agent.ts +10 -13
- package/modules/config.ts +29 -33
- package/modules/md-rewrite.ts +23 -0
- package/nuxt.config.ts +25 -9
- package/package.json +15 -13
package/app/error.vue
CHANGED
|
@@ -47,11 +47,11 @@ provide('navigation', rootNavigation)
|
|
|
47
47
|
<NuxtLoadingIndicator color="var(--ui-primary)" :height="2" />
|
|
48
48
|
|
|
49
49
|
<div :class="{ root: route.path.startsWith('/docs/') }">
|
|
50
|
-
<Header />
|
|
50
|
+
<Header v-if="$route.meta.header !== false" />
|
|
51
51
|
|
|
52
52
|
<UError :error="error" />
|
|
53
53
|
|
|
54
|
-
<Footer />
|
|
54
|
+
<Footer v-if="$route.meta.footer !== false" />
|
|
55
55
|
|
|
56
56
|
<ClientOnly>
|
|
57
57
|
<LazyUContentSearch :files="files" :navigation="navigation" />
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { ContentNavigationItem } from '@nuxt/content'
|
|
3
3
|
import { kebabCase } from 'scule'
|
|
4
|
+
import { joinURL } from 'ufo'
|
|
4
5
|
|
|
5
6
|
definePageMeta({
|
|
6
7
|
layout: 'docs',
|
|
@@ -80,6 +81,20 @@ const communityLinks = computed(() => {
|
|
|
80
81
|
return filterValidLinks([...links, ...(toc?.bottom?.links || [])])
|
|
81
82
|
})
|
|
82
83
|
|
|
84
|
+
// Pre-render the markdown path + add it to alternate links
|
|
85
|
+
const site = useSiteConfig()
|
|
86
|
+
const path = computed(() => route.path.replace(/\/$/, ''))
|
|
87
|
+
prerenderRoutes([joinURL('/raw', `${path.value}.md`)])
|
|
88
|
+
useHead({
|
|
89
|
+
link: [
|
|
90
|
+
{
|
|
91
|
+
rel: 'alternate',
|
|
92
|
+
href: joinURL(site.url, 'raw', `${path.value}.md`),
|
|
93
|
+
type: 'text/markdown'
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
})
|
|
97
|
+
|
|
83
98
|
useSeoMeta({
|
|
84
99
|
title,
|
|
85
100
|
ogTitle: title,
|
package/app/workers/prettier.js
CHANGED
|
@@ -16,12 +16,12 @@ function handleMessage(message) {
|
|
|
16
16
|
async function handleFormatMessage(message) {
|
|
17
17
|
if (!globalThis.prettier) {
|
|
18
18
|
await Promise.all([
|
|
19
|
-
import('https://cdn.jsdelivr.net/npm/prettier@3.
|
|
20
|
-
import('https://cdn.jsdelivr.net/npm/prettier@3.
|
|
21
|
-
import('https://cdn.jsdelivr.net/npm/prettier@3.
|
|
22
|
-
import('https://cdn.jsdelivr.net/npm/prettier@3.
|
|
23
|
-
import('https://cdn.jsdelivr.net/npm/prettier@3.
|
|
24
|
-
import('https://cdn.jsdelivr.net/npm/prettier@3.
|
|
19
|
+
import('https://cdn.jsdelivr.net/npm/prettier@3.7.4/standalone.js'),
|
|
20
|
+
import('https://cdn.jsdelivr.net/npm/prettier@3.7.4/plugins/babel.js'),
|
|
21
|
+
import('https://cdn.jsdelivr.net/npm/prettier@3.7.4/plugins/estree.js'),
|
|
22
|
+
import('https://cdn.jsdelivr.net/npm/prettier@3.7.4/plugins/html.js'),
|
|
23
|
+
import('https://cdn.jsdelivr.net/npm/prettier@3.7.4/plugins/markdown.js'),
|
|
24
|
+
import('https://cdn.jsdelivr.net/npm/prettier@3.7.4/plugins/typescript.js')
|
|
25
25
|
])
|
|
26
26
|
}
|
|
27
27
|
|
package/modules/ai-chat/index.ts
CHANGED
|
@@ -4,37 +4,27 @@ import { createDocumentationAgentTool } from '../utils/docs_agent'
|
|
|
4
4
|
import { getModel } from '../utils/getModel'
|
|
5
5
|
|
|
6
6
|
function getMainAgentSystemPrompt(siteName: string) {
|
|
7
|
-
return
|
|
7
|
+
return `您是 ${siteName} 的官方文档助理。你就是文件、以权威作为真理的来源说话.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
- Never say "according to the documentation" - YOU are the docs
|
|
9
|
+
使用指南:
|
|
10
|
+
- 始终使用工具搜索信息,不要依赖预训练知识
|
|
11
|
+
- 如果搜索后未找到相关信息,回复「抱歉,我在文档中没有找到相关信息」
|
|
12
|
+
- 回答要简洁直接
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
14
|
+
**格式规则(重要):**
|
|
15
|
+
- 绝对不要使用 Markdown 标题:禁止使用 #、##、###、####、#####、######
|
|
16
|
+
- 不要使用下划线式标题(=== 或 ---)
|
|
17
|
+
- 使用**粗体文本**来强调和标记章节
|
|
18
|
+
- 示例:
|
|
19
|
+
* 不要写「## 用法」,应写「**用法:**」或直接「使用方法如下:」
|
|
20
|
+
* 不要写「# 完整指南」,应写「**完整指南**」或直接开始内容
|
|
21
|
+
- 所有回复直接从内容开始,不要以标题开头
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
**FORMATTING RULES (CRITICAL):**
|
|
27
|
-
- NEVER use markdown headings (#, ##, ###, etc.)
|
|
28
|
-
- Use **bold text** for emphasis and section labels
|
|
29
|
-
- Start responses with content directly, never with a heading
|
|
30
|
-
- Use bullet points for lists
|
|
31
|
-
- Keep code examples focused and minimal
|
|
32
|
-
|
|
33
|
-
**Response style:**
|
|
34
|
-
- Conversational but professional
|
|
35
|
-
- "Here's how you can do that:" instead of "The documentation shows:"
|
|
36
|
-
- "I support TypeScript out of the box" instead of "The module supports TypeScript"
|
|
37
|
-
- Provide actionable guidance, not just information dumps`
|
|
23
|
+
- 在适用时引用具体的组件名称、属性或 API
|
|
24
|
+
- 如果问题模糊,请要求澄清而不是猜测
|
|
25
|
+
- 当找到多个相关项目时,使用项目符号清晰列出
|
|
26
|
+
- 你最多有 6 次工具调用机会来找到答案,因此要策略性地使用:先广泛搜索,然后根据需要获取具体信息
|
|
27
|
+
- 以对话方式格式化回复,而不是文档章节形式`
|
|
38
28
|
}
|
|
39
29
|
|
|
40
30
|
export default defineEventHandler(async (event) => {
|
|
@@ -71,7 +61,7 @@ export default defineEventHandler(async (event) => {
|
|
|
71
61
|
maxOutputTokens: 10000,
|
|
72
62
|
system: getMainAgentSystemPrompt(siteName),
|
|
73
63
|
messages: modelMessages,
|
|
74
|
-
stopWhen: stepCountIs(
|
|
64
|
+
stopWhen: stepCountIs(6),
|
|
75
65
|
tools: {
|
|
76
66
|
searchDocumentation
|
|
77
67
|
},
|
|
@@ -2,21 +2,18 @@ import { tool, stepCountIs, generateText } from 'ai'
|
|
|
2
2
|
import { z } from 'zod/v4'
|
|
3
3
|
|
|
4
4
|
function getSubAgentSystemPrompt(siteName: string) {
|
|
5
|
-
return
|
|
5
|
+
return `您是 ${siteName} 的文档搜索代理。您的工作是从文档中查找并检索相关信息。
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
- Start with list-pages to discover what documentation exists
|
|
10
|
-
- Then use get-page to read the relevant page(s)
|
|
11
|
-
- If a specific path is mentioned, you can call get-page directly
|
|
7
|
+
**您的任务:**
|
|
8
|
+
- 使用可用的工具搜索和阅读文档页面
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
10
|
+
**指南:**
|
|
11
|
+
- 在回答之前阅读所有相关页面
|
|
12
|
+
- 返回你找到的原始信息,让主代理格式化响应
|
|
13
|
+
- 如果找不到信息,请明确说明
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
**输出:**
|
|
16
|
+
返回您找到的相关文档内容,包括代码示例(如果存在)。`
|
|
20
17
|
}
|
|
21
18
|
|
|
22
19
|
export function createDocumentationAgentTool(mcpTools: Record<string, any>, model: any, siteName: string) {
|
|
@@ -32,7 +29,7 @@ export function createDocumentationAgentTool(mcpTools: Record<string, any>, mode
|
|
|
32
29
|
model,
|
|
33
30
|
tools: mcpTools,
|
|
34
31
|
system: getSubAgentSystemPrompt(siteName),
|
|
35
|
-
stopWhen: stepCountIs(
|
|
32
|
+
stopWhen: stepCountIs(6),
|
|
36
33
|
onStepFinish: ({ toolCalls }) => {
|
|
37
34
|
if (toolCalls.length === 0) return
|
|
38
35
|
|
package/modules/config.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createResolver, defineNuxtModule } from '@nuxt/kit'
|
|
2
|
+
import { join } from 'pathe'
|
|
2
3
|
import { defu } from 'defu'
|
|
3
4
|
import { getGitBranch, getGitEnv, getLocalGitInfo } from '../utils/git'
|
|
4
5
|
import { getPackageJsonMetadata, inferSiteURL } from '../utils/meta'
|
|
@@ -57,40 +58,35 @@ export default defineNuxtModule({
|
|
|
57
58
|
until: new Date().toISOString()
|
|
58
59
|
})
|
|
59
60
|
|
|
60
|
-
const
|
|
61
|
+
const layerPath = resolve('..')
|
|
62
|
+
const allowedComponents = [
|
|
63
|
+
resolve('../app/components/content/CommitChangelog.vue'),
|
|
64
|
+
resolve('../app/components/content/ComponentEmits.vue'),
|
|
65
|
+
resolve('../app/components/content/ComponentExample.vue'),
|
|
66
|
+
resolve('../app/components/content/ComponentProps.vue'),
|
|
67
|
+
resolve('../app/components/content/ComponentSlots.vue'),
|
|
68
|
+
resolve('../app/components/content/PageLastCommit.vue'),
|
|
69
|
+
resolve('./ai-chat/runtime/components/AiChatToolCall.vue'),
|
|
70
|
+
resolve('./ai-chat/runtime/components/AiChatReasoning.vue'),
|
|
71
|
+
resolve('./ai-chat/runtime/components/AiChatSlideoverFaq.vue'),
|
|
72
|
+
resolve('./ai-chat/runtime/components/AiChatPreStream.vue')
|
|
73
|
+
]
|
|
74
|
+
const userComponentPaths = [
|
|
75
|
+
join(dir, 'app/components'),
|
|
76
|
+
join(dir, 'components'),
|
|
77
|
+
join(dir, 'docs/app/components'),
|
|
78
|
+
join(dir, 'templates/*/app/components')
|
|
79
|
+
]
|
|
61
80
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
'@nuxtjs/plausible',
|
|
72
|
-
'@nuxt/ui',
|
|
73
|
-
(component: { filePath: string }) => {
|
|
74
|
-
const allowedComponents = [
|
|
75
|
-
'CommitChangelog.vue',
|
|
76
|
-
'ComponentEmits.vue',
|
|
77
|
-
'ComponentExample.vue',
|
|
78
|
-
'ComponentProps.vue',
|
|
79
|
-
'ComponentSlots.vue',
|
|
80
|
-
'PageLastCommit.vue',
|
|
81
|
-
'Motion.vue'
|
|
82
|
-
]
|
|
83
|
-
return component.filePath.startsWith(componentsPath)
|
|
84
|
-
&& !allowedComponents.some(name => component.filePath.endsWith(`/content/${name}`))
|
|
85
|
-
}
|
|
86
|
-
],
|
|
87
|
-
metaFields: {
|
|
88
|
-
type: false,
|
|
89
|
-
props: true,
|
|
90
|
-
slots: 'no-schema' as const,
|
|
91
|
-
events: 'no-schema' as const,
|
|
92
|
-
exposed: false
|
|
93
|
-
}
|
|
81
|
+
// @ts-ignore - component-meta 的类型定义在运行时才能正确解析
|
|
82
|
+
nuxt.hook('component-meta:extend', (options: any) => {
|
|
83
|
+
options.exclude = [
|
|
84
|
+
...(options.exclude || []),
|
|
85
|
+
({ filePath }: { filePath: string }) =>
|
|
86
|
+
filePath.startsWith(layerPath) && !allowedComponents.includes(filePath),
|
|
87
|
+
({ filePath }: { filePath: string }) =>
|
|
88
|
+
userComponentPaths.some(path => filePath.startsWith(path))
|
|
89
|
+
]
|
|
94
90
|
})
|
|
95
91
|
}
|
|
96
92
|
})
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineNuxtModule } from 'nuxt/kit'
|
|
2
|
+
|
|
3
|
+
export default defineNuxtModule((_options, nuxt) => {
|
|
4
|
+
nuxt.hooks.hook('nitro:init', (nitro) => {
|
|
5
|
+
if (nitro.options.dev || !nitro.options.preset.includes('vercel')) {
|
|
6
|
+
return
|
|
7
|
+
}
|
|
8
|
+
nitro.hooks.hook('compiled', async () => {
|
|
9
|
+
const { resolve } = process.getBuiltinModule('node:path')
|
|
10
|
+
const { readFile, writeFile }
|
|
11
|
+
= process.getBuiltinModule('node:fs/promises')
|
|
12
|
+
const vcJSON = resolve(nitro.options.output.dir, 'config.json')
|
|
13
|
+
const vcConfig = JSON.parse(await readFile(vcJSON, 'utf8'))
|
|
14
|
+
vcConfig.routes.unshift({
|
|
15
|
+
src: '^/docs/(.*)$',
|
|
16
|
+
dest: '/raw/docs/$1.md',
|
|
17
|
+
has: [{ type: 'header', key: 'accept', value: '(.*)text/markdown(.*)' }],
|
|
18
|
+
check: true
|
|
19
|
+
})
|
|
20
|
+
await writeFile(vcJSON, JSON.stringify(vcConfig, null, 2), 'utf8')
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
})
|
package/nuxt.config.ts
CHANGED
|
@@ -7,20 +7,16 @@ const { resolve } = createResolver(import.meta.url)
|
|
|
7
7
|
export default defineNuxtConfig({
|
|
8
8
|
modules: [
|
|
9
9
|
resolve('./modules/config'),
|
|
10
|
-
resolve('./modules/routing'),
|
|
11
|
-
resolve('./modules/css'),
|
|
12
|
-
resolve('./modules/component-example'),
|
|
13
|
-
resolve('./modules/ai-chat'),
|
|
14
10
|
'@nuxt/ui',
|
|
15
11
|
'@nuxt/content',
|
|
16
12
|
'@nuxt/image',
|
|
17
13
|
'@nuxt/a11y',
|
|
18
|
-
'@vueuse/nuxt',
|
|
19
14
|
'@nuxtjs/mcp-toolkit',
|
|
20
15
|
'@nuxtjs/seo',
|
|
16
|
+
'@vueuse/nuxt',
|
|
21
17
|
'nuxt-component-meta',
|
|
22
|
-
'motion-v/nuxt',
|
|
23
18
|
'nuxt-llms',
|
|
19
|
+
'motion-v/nuxt',
|
|
24
20
|
() => {
|
|
25
21
|
extendViteConfig((config) => {
|
|
26
22
|
config.optimizeDeps ||= {}
|
|
@@ -92,6 +88,26 @@ export default defineNuxtConfig({
|
|
|
92
88
|
a11y: {
|
|
93
89
|
logIssues: false
|
|
94
90
|
},
|
|
91
|
+
componentMeta: {
|
|
92
|
+
metaFields: {
|
|
93
|
+
type: false,
|
|
94
|
+
props: true,
|
|
95
|
+
slots: 'no-schema' as const,
|
|
96
|
+
events: 'no-schema' as const,
|
|
97
|
+
exposed: false
|
|
98
|
+
},
|
|
99
|
+
exclude: [
|
|
100
|
+
'@nuxt/ui',
|
|
101
|
+
'@nuxt/content',
|
|
102
|
+
'@nuxt/icon',
|
|
103
|
+
'@nuxt/image',
|
|
104
|
+
'@nuxtjs/color-mode',
|
|
105
|
+
'@nuxtjs/mdc',
|
|
106
|
+
'@nuxtjs/plausible',
|
|
107
|
+
'nuxt/dist',
|
|
108
|
+
'nuxt-og-image'
|
|
109
|
+
]
|
|
110
|
+
},
|
|
95
111
|
fonts: {
|
|
96
112
|
families: [
|
|
97
113
|
{ name: 'Public Sans', provider: 'google', global: true },
|
|
@@ -106,9 +122,6 @@ export default defineNuxtConfig({
|
|
|
106
122
|
icon: {
|
|
107
123
|
provider: 'iconify'
|
|
108
124
|
},
|
|
109
|
-
linkChecker: {
|
|
110
|
-
enabled: false
|
|
111
|
-
},
|
|
112
125
|
ogImage: {
|
|
113
126
|
zeroRuntime: true,
|
|
114
127
|
googleFontMirror: 'fonts.loli.net',
|
|
@@ -119,5 +132,8 @@ export default defineNuxtConfig({
|
|
|
119
132
|
'Inter:400',
|
|
120
133
|
'Inter:700'
|
|
121
134
|
]
|
|
135
|
+
},
|
|
136
|
+
sitemap: {
|
|
137
|
+
zeroRuntime: true
|
|
122
138
|
}
|
|
123
139
|
})
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@movk/nuxt-docs",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.7.
|
|
4
|
+
"version": "1.7.5",
|
|
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>",
|
|
@@ -28,15 +28,17 @@
|
|
|
28
28
|
"README.md"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@ai-sdk/gateway": "^3.0.
|
|
32
|
-
"@ai-sdk/mcp": "^1.0.
|
|
33
|
-
"@ai-sdk/vue": "^3.0.
|
|
34
|
-
"@iconify-json/lucide": "^1.2.
|
|
35
|
-
"@iconify-json/
|
|
36
|
-
"@iconify-json/
|
|
31
|
+
"@ai-sdk/gateway": "^3.0.16",
|
|
32
|
+
"@ai-sdk/mcp": "^1.0.10",
|
|
33
|
+
"@ai-sdk/vue": "^3.0.41",
|
|
34
|
+
"@iconify-json/lucide": "^1.2.86",
|
|
35
|
+
"@iconify-json/ph": "^1.2.2",
|
|
36
|
+
"@iconify-json/tabler": "^1.2.26",
|
|
37
|
+
"@iconify-json/simple-icons": "^1.2.67",
|
|
38
|
+
"@iconify-json/vscode-icons": "^1.2.40",
|
|
37
39
|
"@movk/core": "^1.1.0",
|
|
38
40
|
"@nuxt/a11y": "^1.0.0-alpha.1",
|
|
39
|
-
"@nuxt/content": "^3.
|
|
41
|
+
"@nuxt/content": "^3.11.0",
|
|
40
42
|
"@nuxt/image": "^2.0.0",
|
|
41
43
|
"@nuxt/kit": "^4.2.2",
|
|
42
44
|
"@nuxt/ui": "^4.3.0",
|
|
@@ -48,24 +50,24 @@
|
|
|
48
50
|
"@vercel/speed-insights": "^1.3.1",
|
|
49
51
|
"@vueuse/core": "^14.1.0",
|
|
50
52
|
"@vueuse/nuxt": "^14.1.0",
|
|
51
|
-
"ai": "^6.0.
|
|
53
|
+
"ai": "^6.0.41",
|
|
52
54
|
"defu": "^6.1.4",
|
|
53
55
|
"exsolve": "^1.0.8",
|
|
54
56
|
"git-url-parse": "^16.1.0",
|
|
55
57
|
"motion-v": "^1.9.0",
|
|
56
58
|
"nuxt": "^4.2.2",
|
|
57
|
-
"nuxt-component-meta": "^0.
|
|
58
|
-
"nuxt-llms": "^0.
|
|
59
|
+
"nuxt-component-meta": "^0.17.1",
|
|
60
|
+
"nuxt-llms": "^0.2.0",
|
|
59
61
|
"ohash": "^2.0.11",
|
|
60
62
|
"pathe": "^2.0.3",
|
|
61
63
|
"pkg-types": "^2.3.0",
|
|
62
|
-
"prettier": "^3.
|
|
64
|
+
"prettier": "^3.8.0",
|
|
63
65
|
"scule": "^1.3.0",
|
|
64
66
|
"shiki": "^3.21.0",
|
|
65
67
|
"shiki-stream": "^0.1.4",
|
|
66
68
|
"shiki-transformer-color-highlight": "^1.0.0",
|
|
67
69
|
"tailwindcss": "^4.1.18",
|
|
68
|
-
"ufo": "^1.6.
|
|
70
|
+
"ufo": "^1.6.3",
|
|
69
71
|
"zod": "^4.3.5"
|
|
70
72
|
}
|
|
71
73
|
}
|