@movk/nuxt-docs 1.14.1 → 1.15.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.
- package/README.md +1 -1
- package/app/app.config.ts +5 -32
- package/app/app.vue +54 -32
- package/app/components/DocsAsideRightBottom.vue +1 -1
- package/app/components/OgImage/OgImageDocs.takumi.vue +90 -0
- package/app/components/PageHeaderLinks.vue +6 -16
- package/app/components/content/CommitChangelog.vue +1 -0
- package/app/components/content/Mermaid.vue +3 -1
- package/app/components/header/HeaderCTA.vue +1 -10
- package/app/components/theme-picker/ThemePicker.vue +22 -33
- package/app/composables/useTheme.ts +64 -84
- package/app/composables/useToolCall.ts +5 -8
- package/app/error.vue +1 -1
- package/app/pages/docs/[...slug].vue +6 -6
- package/app/plugins/theme.ts +39 -68
- package/app/templates/landing.vue +5 -2
- package/app/templates/releases.vue +5 -2
- package/app/types/index.d.ts +8 -57
- package/content.config.ts +1 -0
- package/modules/ai-chat/index.ts +16 -26
- package/modules/ai-chat/runtime/components/AiChat.vue +3 -3
- package/modules/ai-chat/runtime/components/AiChatFloatingInput.vue +8 -8
- package/modules/ai-chat/runtime/components/AiChatPanel.vue +216 -231
- package/modules/ai-chat/runtime/components/AiChatPreStream.vue +0 -14
- package/modules/ai-chat/runtime/composables/useAIChat.ts +25 -73
- package/modules/ai-chat/runtime/composables/useModels.ts +0 -19
- package/modules/ai-chat/runtime/server/api/ai-chat.ts +74 -48
- package/modules/ai-chat/runtime/server/utils/getModel.ts +1 -9
- package/modules/ai-chat/runtime/types.ts +5 -0
- package/nuxt.config.ts +42 -36
- package/nuxt.schema.ts +14 -99
- package/package.json +25 -29
- package/server/mcp/tools/get-page.ts +5 -47
- package/server/mcp/tools/list-getting-started-guides.ts +1 -3
- package/server/mcp/tools/list-pages.ts +9 -44
- package/utils/git.ts +26 -79
- package/app/components/OgImage/Nuxt.vue +0 -247
- package/app/composables/useAnalytics.ts +0 -7
- package/modules/ai-chat/runtime/components/AiChatReasoning.vue +0 -49
- package/modules/ai-chat/runtime/components/AiChatSlideoverFaq.vue +0 -38
- package/modules/ai-chat/runtime/components/AiChatToolCall.vue +0 -31
- package/modules/ai-chat/runtime/server/utils/docs_agent.ts +0 -54
|
@@ -5,28 +5,14 @@ import { useHighlighter } from '../composables/useHighlighter'
|
|
|
5
5
|
const colorMode = useColorMode()
|
|
6
6
|
const highlighter = await useHighlighter()
|
|
7
7
|
const props = defineProps<{
|
|
8
|
-
/**
|
|
9
|
-
* 要高亮显示的代码内容
|
|
10
|
-
*/
|
|
11
8
|
code: string
|
|
12
|
-
/**
|
|
13
|
-
* 代码语言(如 'vue'、'javascript'、'typescript')
|
|
14
|
-
*/
|
|
15
9
|
language: string
|
|
16
|
-
/**
|
|
17
|
-
* 自定义 CSS 类名
|
|
18
|
-
*/
|
|
19
10
|
class?: string
|
|
20
|
-
/**
|
|
21
|
-
* 代码块元数据
|
|
22
|
-
*/
|
|
23
11
|
meta?: string
|
|
24
12
|
}>()
|
|
25
|
-
|
|
26
13
|
const trimmedCode = computed(() => {
|
|
27
14
|
return props.code.trim().replace(/`+$/, '')
|
|
28
15
|
})
|
|
29
|
-
|
|
30
16
|
const lang = computed(() => {
|
|
31
17
|
switch (props.language) {
|
|
32
18
|
case 'vue':
|
|
@@ -1,93 +1,45 @@
|
|
|
1
1
|
import type { UIMessage } from 'ai'
|
|
2
|
-
import {
|
|
3
|
-
import type { FaqCategory, FaqQuestions } from '../types'
|
|
2
|
+
import { createSharedComposable, useLocalStorage } from '@vueuse/core'
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
if (!questions || (Array.isArray(questions) && questions.length === 0)) {
|
|
7
|
-
return []
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
if (typeof questions[0] === 'string') {
|
|
11
|
-
return [{
|
|
12
|
-
category: '问题',
|
|
13
|
-
items: questions as string[]
|
|
14
|
-
}]
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return questions as FaqCategory[]
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const PANEL_WIDTH_COMPACT = 360
|
|
21
|
-
const PANEL_WIDTH_EXPANDED = 520
|
|
22
|
-
|
|
23
|
-
export function useAIChat() {
|
|
4
|
+
export const useAIChat = createSharedComposable(() => {
|
|
24
5
|
const config = useRuntimeConfig()
|
|
25
|
-
const
|
|
26
|
-
const isEnabled = computed(() => config.public.aiChat?.enabled ?? false)
|
|
6
|
+
const site = useSiteConfig()
|
|
27
7
|
|
|
28
|
-
const
|
|
29
|
-
const isExpanded = useState('ai-chat-expanded', () => false)
|
|
30
|
-
const messages = useState<UIMessage[]>('ai-chat-messages', () => [])
|
|
31
|
-
const pendingMessage = useState<string | undefined>('ai-chat-pending', () => undefined)
|
|
8
|
+
const isEnabled = computed(() => config.public.aiChat?.enabled ?? false)
|
|
32
9
|
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
const shouldPushContent = computed(() => !isMobile.value && isOpen.value)
|
|
10
|
+
const storageOpen = useLocalStorage(`${site.name}-ai-chat-open`, false)
|
|
11
|
+
const messages = useLocalStorage<UIMessage[]>(`${site.name}-ai-chat-messages`, [])
|
|
36
12
|
|
|
37
|
-
const
|
|
38
|
-
const aiChatConfig = appConfig.aiChat
|
|
39
|
-
const faqConfig = aiChatConfig?.faqQuestions
|
|
40
|
-
if (!faqConfig) return []
|
|
13
|
+
const isOpen = ref(false)
|
|
41
14
|
|
|
42
|
-
|
|
15
|
+
onNuxtReady(() => {
|
|
16
|
+
nextTick(() => {
|
|
17
|
+
isOpen.value = storageOpen.value
|
|
18
|
+
})
|
|
43
19
|
})
|
|
44
20
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (initialMessage) {
|
|
51
|
-
pendingMessage.value = initialMessage
|
|
52
|
-
}
|
|
53
|
-
isOpen.value = true
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function clearPending() {
|
|
57
|
-
pendingMessage.value = undefined
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function close() {
|
|
61
|
-
isOpen.value = false
|
|
62
|
-
}
|
|
21
|
+
watch(isOpen, (value) => {
|
|
22
|
+
storageOpen.value = value
|
|
23
|
+
})
|
|
63
24
|
|
|
64
|
-
function
|
|
25
|
+
function toggleChat() {
|
|
65
26
|
isOpen.value = !isOpen.value
|
|
66
27
|
}
|
|
67
28
|
|
|
68
|
-
function
|
|
69
|
-
messages.value = [
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
29
|
+
function open(text: string) {
|
|
30
|
+
messages.value = [...messages.value, {
|
|
31
|
+
id: String(Date.now()),
|
|
32
|
+
role: 'user',
|
|
33
|
+
parts: [{ type: 'text', text: text }]
|
|
34
|
+
}]
|
|
35
|
+
isOpen.value = true
|
|
74
36
|
}
|
|
75
37
|
|
|
76
38
|
return {
|
|
77
39
|
isEnabled,
|
|
78
40
|
isOpen,
|
|
79
|
-
isExpanded,
|
|
80
|
-
isMobile,
|
|
81
|
-
panelWidth,
|
|
82
|
-
shouldPushContent,
|
|
83
41
|
messages,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
open,
|
|
87
|
-
clearPending,
|
|
88
|
-
close,
|
|
89
|
-
toggle,
|
|
90
|
-
toggleExpanded,
|
|
91
|
-
clearMessages
|
|
42
|
+
toggleChat,
|
|
43
|
+
open
|
|
92
44
|
}
|
|
93
|
-
}
|
|
45
|
+
})
|
|
@@ -13,25 +13,6 @@ export function useModels() {
|
|
|
13
13
|
function formatModelName(modelId: string): string {
|
|
14
14
|
const acronyms = ['gpt', 'llm', 'ai'] // words that should be uppercase
|
|
15
15
|
|
|
16
|
-
// 处理 OpenRouter 模型格式:openrouter/provider/model
|
|
17
|
-
if (modelId.startsWith('openrouter/')) {
|
|
18
|
-
const parts = modelId.split('/')
|
|
19
|
-
const model = parts[2] || parts[1] || modelId
|
|
20
|
-
|
|
21
|
-
// 提取模型名称(去除版本号和特殊标记)
|
|
22
|
-
const modelName = model.split(':')[0] || model
|
|
23
|
-
|
|
24
|
-
return modelName
|
|
25
|
-
.split('-')
|
|
26
|
-
.map((word) => {
|
|
27
|
-
const lowerWord = word.toLowerCase()
|
|
28
|
-
return acronyms.includes(lowerWord)
|
|
29
|
-
? word.toUpperCase()
|
|
30
|
-
: word.charAt(0).toUpperCase() + word.slice(1)
|
|
31
|
-
})
|
|
32
|
-
.join(' ')
|
|
33
|
-
}
|
|
34
|
-
|
|
35
16
|
// 处理常规模型格式:provider/model
|
|
36
17
|
const modelName = modelId.split('/')[1] || modelId
|
|
37
18
|
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import { streamText, convertToModelMessages, stepCountIs,
|
|
1
|
+
import { streamText, convertToModelMessages, stepCountIs, smoothStream } from 'ai'
|
|
2
2
|
import { createMCPClient } from '@ai-sdk/mcp'
|
|
3
|
-
import { createDocumentationAgentTool } from '../utils/docs_agent'
|
|
4
3
|
import { getModel } from '../utils/getModel'
|
|
5
|
-
import { inferSiteURL } from '../../../../../utils/meta'
|
|
6
4
|
|
|
7
5
|
function getMainAgentSystemPrompt(siteName: string) {
|
|
8
|
-
return `您是 ${siteName}
|
|
6
|
+
return `您是 ${siteName} 的官方文档助理,你就是文件、以权威作为真理的来源说话。
|
|
9
7
|
|
|
10
8
|
使用指南:
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
9
|
+
- 对于文档问题,请始终使用工具搜索信息,不要依赖预训练知识。
|
|
10
|
+
- 如果用户的问题与文档无关,请尽可能简短地回答,但不要浪费工具调用来搜索文档。
|
|
11
|
+
- 如果搜索后没有找到相关信息,请回复“抱歉,我在文档中找不到相关信息。”
|
|
12
|
+
- 您的回答要简洁、直接。
|
|
14
13
|
|
|
15
14
|
**格式规则(重要):**
|
|
16
15
|
- 绝对不要使用 Markdown 标题:禁止使用 #、##、###、####、#####、######
|
|
@@ -21,64 +20,91 @@ function getMainAgentSystemPrompt(siteName: string) {
|
|
|
21
20
|
* 不要写「# 完整指南」,应写「**完整指南**」或直接开始内容
|
|
22
21
|
- 所有回复直接从内容开始,不要以标题开头
|
|
23
22
|
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
23
|
+
- 在适用时引用具体的组件名称、props 或 API。
|
|
24
|
+
- 如果问题不明确,请要求澄清而不是猜测。
|
|
25
|
+
- 当发现多个相关项目时,使用要点清楚地列出它们。
|
|
26
|
+
- 要策略性地使用工具:先广泛搜索,然后根据需要获取具体信息。
|
|
28
27
|
- 以对话方式格式化回复,而不是文档章节形式`
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
export default defineEventHandler(async (event) => {
|
|
32
|
-
|
|
31
|
+
if (!process.env.AI_GATEWAY_API_KEY) {
|
|
32
|
+
throw createError({ statusCode: 503, message: 'AI Chat is not configured.' })
|
|
33
|
+
}
|
|
34
|
+
|
|
33
35
|
const config = useRuntimeConfig()
|
|
36
|
+
|
|
37
|
+
const { messages, model: requestModel } = await readBody(event)
|
|
38
|
+
|
|
39
|
+
if (!messages || !Array.isArray(messages)) {
|
|
40
|
+
throw createError({ statusCode: 400, message: 'Invalid or missing messages array.' })
|
|
41
|
+
}
|
|
34
42
|
const siteConfig = getSiteConfig(event)
|
|
35
43
|
const siteName = siteConfig.name || 'Documentation'
|
|
36
44
|
|
|
37
45
|
const mcpPath = config.aiChat.mcpPath
|
|
38
46
|
const isExternalUrl = mcpPath.startsWith('http://') || mcpPath.startsWith('https://')
|
|
39
47
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
let httpClient
|
|
49
|
+
let mcpTools
|
|
50
|
+
try {
|
|
51
|
+
const mcpUrl = isExternalUrl
|
|
52
|
+
? mcpPath
|
|
53
|
+
: `${getRequestURL(event).origin}${mcpPath}`
|
|
45
54
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const mcpTools = await httpClient.tools()
|
|
55
|
+
httpClient = await createMCPClient({
|
|
56
|
+
transport: { type: 'http', url: mcpUrl }
|
|
57
|
+
})
|
|
58
|
+
mcpTools = await httpClient.tools()
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error('MCP client error:', error)
|
|
53
61
|
|
|
54
|
-
|
|
62
|
+
throw createError({
|
|
63
|
+
statusCode: 503,
|
|
64
|
+
message: 'Unable to connect to the documentation service. Please try again later.'
|
|
65
|
+
})
|
|
66
|
+
}
|
|
55
67
|
|
|
56
|
-
const
|
|
68
|
+
const model = getModel(requestModel || config.public.aiChat.model)
|
|
57
69
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
messages: modelMessages,
|
|
66
|
-
stopWhen: stepCountIs(6),
|
|
67
|
-
tools: {
|
|
68
|
-
searchDocumentation
|
|
69
|
-
},
|
|
70
|
-
experimental_context: {
|
|
71
|
-
writer
|
|
70
|
+
return streamText({
|
|
71
|
+
model,
|
|
72
|
+
maxOutputTokens: 16000,
|
|
73
|
+
providerOptions: {
|
|
74
|
+
anthropic: {
|
|
75
|
+
thinking: {
|
|
76
|
+
type: 'adaptive'
|
|
72
77
|
},
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
+
effort: 'low'
|
|
79
|
+
},
|
|
80
|
+
gateway: {
|
|
81
|
+
caching: 'auto'
|
|
82
|
+
},
|
|
83
|
+
google: {
|
|
84
|
+
thinkingConfig: {
|
|
85
|
+
includeThoughts: true,
|
|
86
|
+
thinkingLevel: 'low'
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
openai: {
|
|
90
|
+
reasoningEffort: 'low',
|
|
91
|
+
reasoningSummary: 'detailed'
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
system: getMainAgentSystemPrompt(siteName),
|
|
95
|
+
messages: await convertToModelMessages(messages),
|
|
96
|
+
experimental_transform: smoothStream(),
|
|
97
|
+
stopWhen: stepCountIs(8),
|
|
98
|
+
tools: {
|
|
99
|
+
...mcpTools
|
|
78
100
|
},
|
|
79
101
|
onFinish: async () => {
|
|
80
|
-
|
|
102
|
+
event.waitUntil(httpClient?.close())
|
|
103
|
+
},
|
|
104
|
+
onError: (error) => {
|
|
105
|
+
console.error('streamText error:', error)
|
|
106
|
+
|
|
107
|
+
event.waitUntil(httpClient?.close())
|
|
81
108
|
}
|
|
82
|
-
})
|
|
83
|
-
return createUIMessageStreamResponse({ stream })
|
|
109
|
+
}).toUIMessageStreamResponse()
|
|
84
110
|
})
|
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import { createGateway } from '@ai-sdk/gateway'
|
|
2
|
-
import { createOpenRouter } from '@openrouter/ai-sdk-provider'
|
|
3
2
|
import { modelProviderRegistry } from './modelProviders'
|
|
4
3
|
|
|
5
|
-
modelProviderRegistry.register('openrouter', ({ config, modelId }) => {
|
|
6
|
-
const openRouter = createOpenRouter({
|
|
7
|
-
apiKey: config.openRouterApiKey as string | undefined
|
|
8
|
-
})
|
|
9
|
-
return openRouter.chat(modelId)
|
|
10
|
-
})
|
|
11
|
-
|
|
12
4
|
/**
|
|
13
5
|
* 获取 AI 模型实例
|
|
14
6
|
* 优先使用注册的提供商,否则回退到 AI Gateway
|
|
@@ -25,7 +17,7 @@ export function getModel(modelId: string) {
|
|
|
25
17
|
}
|
|
26
18
|
|
|
27
19
|
const gateway = createGateway({
|
|
28
|
-
apiKey:
|
|
20
|
+
apiKey: process.env.AI_GATEWAY_API_KEY || undefined
|
|
29
21
|
})
|
|
30
22
|
return gateway(modelId)
|
|
31
23
|
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import type { DynamicToolUIPart, ToolUIPart } from 'ai'
|
|
2
|
+
|
|
1
3
|
export interface FaqCategory {
|
|
2
4
|
category: string
|
|
3
5
|
items: string[]
|
|
4
6
|
}
|
|
5
7
|
|
|
6
8
|
export type FaqQuestions = string[] | FaqCategory[]
|
|
9
|
+
|
|
10
|
+
export type ToolPart = ToolUIPart | DynamicToolUIPart
|
|
11
|
+
export type ToolState = ToolPart['state']
|
package/nuxt.config.ts
CHANGED
|
@@ -17,6 +17,7 @@ export default defineNuxtConfig({
|
|
|
17
17
|
})
|
|
18
18
|
},
|
|
19
19
|
'@nuxt/ui',
|
|
20
|
+
'@nuxt/fonts',
|
|
20
21
|
'@nuxt/content',
|
|
21
22
|
'@nuxt/image',
|
|
22
23
|
'@nuxt/a11y',
|
|
@@ -65,7 +66,14 @@ export default defineNuxtConfig({
|
|
|
65
66
|
colors: ['primary', 'secondary', 'info', 'success', 'warning', 'error', 'important']
|
|
66
67
|
},
|
|
67
68
|
experimental: {
|
|
68
|
-
componentDetection:
|
|
69
|
+
componentDetection: [
|
|
70
|
+
'Sidebar',
|
|
71
|
+
'ChatMessages',
|
|
72
|
+
'ChatPrompt',
|
|
73
|
+
'ChatPromptSubmit',
|
|
74
|
+
'ChatReasoning',
|
|
75
|
+
'ChatTool'
|
|
76
|
+
]
|
|
69
77
|
}
|
|
70
78
|
},
|
|
71
79
|
|
|
@@ -91,17 +99,12 @@ export default defineNuxtConfig({
|
|
|
91
99
|
crawlLinks: true,
|
|
92
100
|
failOnError: false,
|
|
93
101
|
autoSubfolderIndex: false
|
|
94
|
-
},
|
|
95
|
-
compatibilityDate: {
|
|
96
|
-
// Don't generate observability routes for now
|
|
97
|
-
vercel: '2025-07-14'
|
|
98
102
|
}
|
|
99
103
|
},
|
|
100
104
|
|
|
101
105
|
vite: {
|
|
102
106
|
build: {
|
|
103
|
-
sourcemap: false
|
|
104
|
-
chunkSizeWarningLimit: 1024
|
|
107
|
+
sourcemap: false
|
|
105
108
|
}
|
|
106
109
|
},
|
|
107
110
|
|
|
@@ -109,22 +112,37 @@ export default defineNuxtConfig({
|
|
|
109
112
|
|
|
110
113
|
hooks: {
|
|
111
114
|
'vite:extendConfig': async (config) => {
|
|
112
|
-
// Ensure optimizeDeps.include exists
|
|
113
115
|
const cfg = config as { optimizeDeps?: { include?: string[] } }
|
|
114
|
-
cfg.optimizeDeps
|
|
115
|
-
cfg.optimizeDeps.include
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
include.push(
|
|
125
|
-
'@movk/nuxt-docs > @nuxt/content > slugify',
|
|
126
|
-
'@movk/nuxt-docs > @ai-sdk/gateway > @vercel/oidc'
|
|
116
|
+
cfg.optimizeDeps ||= {}
|
|
117
|
+
cfg.optimizeDeps.include ||= []
|
|
118
|
+
|
|
119
|
+
// tailwindcss/colors is a peer dep resolved in the consumer project directly.
|
|
120
|
+
cfg.optimizeDeps.include.push(
|
|
121
|
+
'tailwindcss/colors',
|
|
122
|
+
'@movk/nuxt-docs > @movk/core',
|
|
123
|
+
'@movk/nuxt-docs > prettier',
|
|
124
|
+
'@movk/nuxt-docs > reka-ui'
|
|
127
125
|
)
|
|
126
|
+
|
|
127
|
+
// AI Chat static deps — only pre-bundle when the feature is actually enabled.
|
|
128
|
+
// @shikijs/langs/* and @shikijs/themes/* are dynamically imported in useHighlighter.ts
|
|
129
|
+
if (process.env.AI_GATEWAY_API_KEY) {
|
|
130
|
+
cfg.optimizeDeps.include.push(
|
|
131
|
+
'@movk/nuxt-docs > @ai-sdk/vue',
|
|
132
|
+
'@movk/nuxt-docs > ai',
|
|
133
|
+
'@movk/nuxt-docs > shiki-stream/vue',
|
|
134
|
+
'@movk/nuxt-docs > @shikijs/core',
|
|
135
|
+
'@movk/nuxt-docs > @shikijs/engine-javascript'
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Transform all remaining 'pkg > dep' entries added by Nuxt modules
|
|
140
|
+
// (e.g. @nuxt/a11y > axe-core, @nuxtjs/mdc > remark-gfm) to use the
|
|
141
|
+
cfg.optimizeDeps.include = cfg.optimizeDeps.include
|
|
142
|
+
.map(id => (id.startsWith('@movk/nuxt-docs > ') || !id.includes(' > '))
|
|
143
|
+
? id
|
|
144
|
+
: `@movk/nuxt-docs > ${id}`
|
|
145
|
+
)
|
|
128
146
|
}
|
|
129
147
|
},
|
|
130
148
|
|
|
@@ -154,14 +172,10 @@ export default defineNuxtConfig({
|
|
|
154
172
|
},
|
|
155
173
|
|
|
156
174
|
fonts: {
|
|
175
|
+
provider: 'bunny',
|
|
157
176
|
families: [
|
|
158
|
-
{ name: 'Public Sans',
|
|
159
|
-
{ name: '
|
|
160
|
-
{ name: 'Geist', provider: 'google' },
|
|
161
|
-
{ name: 'Inter', provider: 'google' },
|
|
162
|
-
{ name: 'Poppins', provider: 'google' },
|
|
163
|
-
{ name: 'Outfit', provider: 'google' },
|
|
164
|
-
{ name: 'Raleway', provider: 'google' }
|
|
177
|
+
{ name: 'Public Sans', global: true, fallbacks: ['Noto Sans SC', 'sans-serif'] },
|
|
178
|
+
{ name: 'Noto Sans SC', global: true }
|
|
165
179
|
]
|
|
166
180
|
},
|
|
167
181
|
|
|
@@ -182,13 +196,5 @@ export default defineNuxtConfig({
|
|
|
182
196
|
// Must be defined before @nuxt/content setup,
|
|
183
197
|
// otherwise Content LLMS module will overwrite it in modules:done.
|
|
184
198
|
contentRawMarkdown: false
|
|
185
|
-
},
|
|
186
|
-
|
|
187
|
-
ogImage: {
|
|
188
|
-
zeroRuntime: true,
|
|
189
|
-
fonts: [
|
|
190
|
-
'Noto+Sans+SC:400',
|
|
191
|
-
'Inter:400'
|
|
192
|
-
]
|
|
193
199
|
}
|
|
194
200
|
})
|
package/nuxt.schema.ts
CHANGED
|
@@ -38,28 +38,6 @@ export default defineNuxtSchema({
|
|
|
38
38
|
}
|
|
39
39
|
}),
|
|
40
40
|
|
|
41
|
-
vercelAnalytics: group({
|
|
42
|
-
title: 'Vercel Analytics',
|
|
43
|
-
description: 'Vercel Analytics 配置',
|
|
44
|
-
icon: 'i-lucide-activity',
|
|
45
|
-
fields: {
|
|
46
|
-
enable: field({
|
|
47
|
-
type: 'boolean',
|
|
48
|
-
title: '启用 Analytics',
|
|
49
|
-
description: '是否启用 Vercel Analytics',
|
|
50
|
-
icon: 'i-lucide-toggle-right',
|
|
51
|
-
default: false
|
|
52
|
-
}),
|
|
53
|
-
debug: field({
|
|
54
|
-
type: 'boolean',
|
|
55
|
-
title: '调试模式',
|
|
56
|
-
description: '是否启用调试模式',
|
|
57
|
-
icon: 'i-lucide-bug',
|
|
58
|
-
default: false
|
|
59
|
-
})
|
|
60
|
-
}
|
|
61
|
-
}),
|
|
62
|
-
|
|
63
41
|
seo: group({
|
|
64
42
|
title: 'SEO',
|
|
65
43
|
description: 'SEO 相关配置',
|
|
@@ -171,7 +149,7 @@ export default defineNuxtSchema({
|
|
|
171
149
|
title: '目录标题',
|
|
172
150
|
description: '目录区域的标题',
|
|
173
151
|
icon: 'i-lucide-heading',
|
|
174
|
-
default: '
|
|
152
|
+
default: '页面导航'
|
|
175
153
|
}),
|
|
176
154
|
|
|
177
155
|
bottom: group({
|
|
@@ -366,62 +344,20 @@ export default defineNuxtSchema({
|
|
|
366
344
|
icon: 'i-lucide-heading',
|
|
367
345
|
default: 'AI 助手'
|
|
368
346
|
}),
|
|
369
|
-
collapse: field({
|
|
370
|
-
type: 'string',
|
|
371
|
-
title: '折叠按钮',
|
|
372
|
-
description: '折叠按钮的文本',
|
|
373
|
-
icon: 'i-lucide-chevron-up',
|
|
374
|
-
default: '折叠'
|
|
375
|
-
}),
|
|
376
|
-
expand: field({
|
|
377
|
-
type: 'string',
|
|
378
|
-
title: '展开按钮',
|
|
379
|
-
description: '展开按钮的文本',
|
|
380
|
-
icon: 'i-lucide-chevron-down',
|
|
381
|
-
default: '展开'
|
|
382
|
-
}),
|
|
383
347
|
clearChat: field({
|
|
384
348
|
type: 'string',
|
|
385
349
|
title: '清除聊天记录',
|
|
386
350
|
description: '清除聊天记录按钮的文本',
|
|
387
|
-
icon: 'i-lucide-
|
|
351
|
+
icon: 'i-lucide-list-x',
|
|
388
352
|
default: '清除聊天记录'
|
|
389
353
|
}),
|
|
390
354
|
close: field({
|
|
391
355
|
type: 'string',
|
|
392
356
|
title: '关闭按钮',
|
|
393
357
|
description: '关闭按钮的文本',
|
|
394
|
-
icon: 'i-lucide-
|
|
358
|
+
icon: 'i-lucide-panel-right-close',
|
|
395
359
|
default: '关闭'
|
|
396
360
|
}),
|
|
397
|
-
loading: field({
|
|
398
|
-
type: 'string',
|
|
399
|
-
title: '加载中',
|
|
400
|
-
description: '加载时的提示文本',
|
|
401
|
-
icon: 'i-lucide-loader',
|
|
402
|
-
default: 'Loading...'
|
|
403
|
-
}),
|
|
404
|
-
askAnything: field({
|
|
405
|
-
type: 'string',
|
|
406
|
-
title: '询问提示',
|
|
407
|
-
description: '询问任何事情的文本',
|
|
408
|
-
icon: 'i-lucide-message-circle',
|
|
409
|
-
default: '问我任何事情...'
|
|
410
|
-
}),
|
|
411
|
-
askMeAnythingDescription: field({
|
|
412
|
-
type: 'string',
|
|
413
|
-
title: '询问提示描述',
|
|
414
|
-
description: '询问任何事情的描述文本',
|
|
415
|
-
icon: 'i-lucide-info',
|
|
416
|
-
default: '我可以帮助您浏览文档、解释概念并回答您的问题。'
|
|
417
|
-
}),
|
|
418
|
-
faq: field({
|
|
419
|
-
type: 'string',
|
|
420
|
-
title: 'FAQ 建议',
|
|
421
|
-
description: 'FAQ 建议标题文本',
|
|
422
|
-
icon: 'i-lucide-help-circle',
|
|
423
|
-
default: 'FAQ 建议'
|
|
424
|
-
}),
|
|
425
361
|
placeholder: field({
|
|
426
362
|
type: 'string',
|
|
427
363
|
title: '输入占位符',
|
|
@@ -443,20 +379,6 @@ export default defineNuxtSchema({
|
|
|
443
379
|
icon: 'i-custom-ai',
|
|
444
380
|
default: '与 AI 聊天'
|
|
445
381
|
}),
|
|
446
|
-
streaming: field({
|
|
447
|
-
type: 'string',
|
|
448
|
-
title: '思考中',
|
|
449
|
-
description: '思考时的提示文本',
|
|
450
|
-
icon: 'i-lucide-brain',
|
|
451
|
-
default: '思考中...'
|
|
452
|
-
}),
|
|
453
|
-
streamed: field({
|
|
454
|
-
type: 'string',
|
|
455
|
-
title: '思考完成',
|
|
456
|
-
description: '思考后的提示文本',
|
|
457
|
-
icon: 'i-lucide-check-circle',
|
|
458
|
-
default: '思考过程'
|
|
459
|
-
}),
|
|
460
382
|
explainWithAi: field({
|
|
461
383
|
type: 'string',
|
|
462
384
|
title: '用 AI 解释按钮',
|
|
@@ -472,13 +394,6 @@ export default defineNuxtSchema({
|
|
|
472
394
|
description: 'UI 图标配置',
|
|
473
395
|
icon: 'i-lucide-icon',
|
|
474
396
|
fields: {
|
|
475
|
-
loading: field({
|
|
476
|
-
type: 'string',
|
|
477
|
-
title: '加载图标',
|
|
478
|
-
description: '加载时的图标',
|
|
479
|
-
icon: 'i-lucide-loader',
|
|
480
|
-
default: 'i-lucide-loader'
|
|
481
|
-
}),
|
|
482
397
|
trigger: field({
|
|
483
398
|
type: 'string',
|
|
484
399
|
title: '触发图标',
|
|
@@ -490,29 +405,29 @@ export default defineNuxtSchema({
|
|
|
490
405
|
type: 'string',
|
|
491
406
|
title: '解释图标',
|
|
492
407
|
description: '「用 AI 解释」按钮的图标',
|
|
493
|
-
icon: 'i-lucide-
|
|
494
|
-
default: 'i-lucide-
|
|
408
|
+
icon: 'i-lucide-bot-message-square',
|
|
409
|
+
default: 'i-lucide-bot-message-square'
|
|
495
410
|
}),
|
|
496
|
-
|
|
411
|
+
reasoning: field({
|
|
497
412
|
type: 'string',
|
|
498
|
-
title: '
|
|
499
|
-
description: '
|
|
500
|
-
icon: 'i-lucide-
|
|
501
|
-
default: 'i-lucide-
|
|
413
|
+
title: '推理图标',
|
|
414
|
+
description: '推理/思考过程的图标',
|
|
415
|
+
icon: 'i-lucide-brain',
|
|
416
|
+
default: 'i-lucide-brain'
|
|
502
417
|
}),
|
|
503
418
|
clearChat: field({
|
|
504
419
|
type: 'string',
|
|
505
420
|
title: '清除图标',
|
|
506
421
|
description: '清除聊天记录按钮的图标',
|
|
507
|
-
icon: 'i-lucide-
|
|
508
|
-
default: 'i-lucide-
|
|
422
|
+
icon: 'i-lucide-list-x',
|
|
423
|
+
default: 'i-lucide-list-x'
|
|
509
424
|
}),
|
|
510
425
|
close: field({
|
|
511
426
|
type: 'string',
|
|
512
427
|
title: '关闭图标',
|
|
513
428
|
description: '关闭按钮的图标',
|
|
514
|
-
icon: 'i-lucide-
|
|
515
|
-
default: 'i-lucide-
|
|
429
|
+
icon: 'i-lucide-panel-right-close',
|
|
430
|
+
default: 'i-lucide-panel-right-close'
|
|
516
431
|
}),
|
|
517
432
|
providers: field({
|
|
518
433
|
type: 'object',
|