@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.
Files changed (42) hide show
  1. package/README.md +1 -1
  2. package/app/app.config.ts +5 -32
  3. package/app/app.vue +54 -32
  4. package/app/components/DocsAsideRightBottom.vue +1 -1
  5. package/app/components/OgImage/OgImageDocs.takumi.vue +90 -0
  6. package/app/components/PageHeaderLinks.vue +6 -16
  7. package/app/components/content/CommitChangelog.vue +1 -0
  8. package/app/components/content/Mermaid.vue +3 -1
  9. package/app/components/header/HeaderCTA.vue +1 -10
  10. package/app/components/theme-picker/ThemePicker.vue +22 -33
  11. package/app/composables/useTheme.ts +64 -84
  12. package/app/composables/useToolCall.ts +5 -8
  13. package/app/error.vue +1 -1
  14. package/app/pages/docs/[...slug].vue +6 -6
  15. package/app/plugins/theme.ts +39 -68
  16. package/app/templates/landing.vue +5 -2
  17. package/app/templates/releases.vue +5 -2
  18. package/app/types/index.d.ts +8 -57
  19. package/content.config.ts +1 -0
  20. package/modules/ai-chat/index.ts +16 -26
  21. package/modules/ai-chat/runtime/components/AiChat.vue +3 -3
  22. package/modules/ai-chat/runtime/components/AiChatFloatingInput.vue +8 -8
  23. package/modules/ai-chat/runtime/components/AiChatPanel.vue +216 -231
  24. package/modules/ai-chat/runtime/components/AiChatPreStream.vue +0 -14
  25. package/modules/ai-chat/runtime/composables/useAIChat.ts +25 -73
  26. package/modules/ai-chat/runtime/composables/useModels.ts +0 -19
  27. package/modules/ai-chat/runtime/server/api/ai-chat.ts +74 -48
  28. package/modules/ai-chat/runtime/server/utils/getModel.ts +1 -9
  29. package/modules/ai-chat/runtime/types.ts +5 -0
  30. package/nuxt.config.ts +42 -36
  31. package/nuxt.schema.ts +14 -99
  32. package/package.json +25 -29
  33. package/server/mcp/tools/get-page.ts +5 -47
  34. package/server/mcp/tools/list-getting-started-guides.ts +1 -3
  35. package/server/mcp/tools/list-pages.ts +9 -44
  36. package/utils/git.ts +26 -79
  37. package/app/components/OgImage/Nuxt.vue +0 -247
  38. package/app/composables/useAnalytics.ts +0 -7
  39. package/modules/ai-chat/runtime/components/AiChatReasoning.vue +0 -49
  40. package/modules/ai-chat/runtime/components/AiChatSlideoverFaq.vue +0 -38
  41. package/modules/ai-chat/runtime/components/AiChatToolCall.vue +0 -31
  42. 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 { useMediaQuery } from '@vueuse/core'
3
- import type { FaqCategory, FaqQuestions } from '../types'
2
+ import { createSharedComposable, useLocalStorage } from '@vueuse/core'
4
3
 
5
- function normalizeFaqQuestions(questions: FaqQuestions): FaqCategory[] {
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 appConfig = useAppConfig()
26
- const isEnabled = computed(() => config.public.aiChat?.enabled ?? false)
6
+ const site = useSiteConfig()
27
7
 
28
- const isOpen = useState('ai-chat-open', () => false)
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 isMobile = useMediaQuery('(max-width: 767px)')
34
- const panelWidth = computed(() => isExpanded.value ? PANEL_WIDTH_EXPANDED : PANEL_WIDTH_COMPACT)
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 faqQuestions = computed<FaqCategory[]>(() => {
38
- const aiChatConfig = appConfig.aiChat
39
- const faqConfig = aiChatConfig?.faqQuestions
40
- if (!faqConfig) return []
13
+ const isOpen = ref(false)
41
14
 
42
- return normalizeFaqQuestions(faqConfig)
15
+ onNuxtReady(() => {
16
+ nextTick(() => {
17
+ isOpen.value = storageOpen.value
18
+ })
43
19
  })
44
20
 
45
- function open(initialMessage?: string, clearPrevious = false) {
46
- if (clearPrevious) {
47
- messages.value = []
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 toggle() {
25
+ function toggleChat() {
65
26
  isOpen.value = !isOpen.value
66
27
  }
67
28
 
68
- function clearMessages() {
69
- messages.value = []
70
- }
71
-
72
- function toggleExpanded() {
73
- isExpanded.value = !isExpanded.value
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
- pendingMessage,
85
- faqQuestions,
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, createUIMessageStream, createUIMessageStreamResponse, smoothStream } from 'ai'
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
- - 在适用时引用具体的组件名称、属性或 API
25
- - 如果问题模糊,请要求澄清而不是猜测
26
- - 当找到多个相关项目时,使用项目符号清晰列出
27
- - 你最多有 6 次工具调用机会来找到答案,因此要策略性地使用:先广泛搜索,然后根据需要获取具体信息
23
+ - 在适用时引用具体的组件名称、props API
24
+ - 如果问题不明确,请要求澄清而不是猜测。
25
+ - 当发现多个相关项目时,使用要点清楚地列出它们。
26
+ - 要策略性地使用工具:先广泛搜索,然后根据需要获取具体信息。
28
27
  - 以对话方式格式化回复,而不是文档章节形式`
29
28
  }
30
29
 
31
30
  export default defineEventHandler(async (event) => {
32
- const { messages, model: requestModel } = await readBody(event)
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
- const mcpUrl = isExternalUrl
41
- ? mcpPath
42
- : import.meta.dev
43
- ? `${getRequestURL(event).origin}${mcpPath}`
44
- : `${inferSiteURL()}${mcpPath}`
48
+ let httpClient
49
+ let mcpTools
50
+ try {
51
+ const mcpUrl = isExternalUrl
52
+ ? mcpPath
53
+ : `${getRequestURL(event).origin}${mcpPath}`
45
54
 
46
- const httpClient = await createMCPClient({
47
- transport: {
48
- type: 'http',
49
- url: mcpUrl
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
- const model = getModel(requestModel || config.public.aiChat.model)
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 searchDocumentation = createDocumentationAgentTool(mcpTools, model, siteName)
68
+ const model = getModel(requestModel || config.public.aiChat.model)
57
69
 
58
- const stream = createUIMessageStream({
59
- execute: async ({ writer }) => {
60
- const modelMessages = await convertToModelMessages(messages)
61
- const result = streamText({
62
- model,
63
- maxOutputTokens: 10000,
64
- system: getMainAgentSystemPrompt(siteName),
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
- experimental_transform: smoothStream({ chunking: 'word' })
74
- })
75
- writer.merge(result.toUIMessageStream({
76
- sendReasoning: true
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
- await httpClient.close()
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: config.aiGatewayApiKey as string | undefined
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: true
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
- const include = cfg.optimizeDeps.include
117
-
118
- // Rewrite optimizeDeps paths for layer architecture
119
- const layerPkgs = /^(?:@nuxt\/content|@nuxtjs\/mdc|@nuxt\/a11y) > /
120
- include.forEach((id, i) => {
121
- if (layerPkgs.test(id)) include[i] = `@movk/nuxt-docs > ${id}`
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', provider: 'google', global: true },
159
- { name: 'DM Sans', provider: 'google' },
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-trash-2',
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-x',
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-brain',
494
- default: 'i-lucide-brain'
408
+ icon: 'i-lucide-bot-message-square',
409
+ default: 'i-lucide-bot-message-square'
495
410
  }),
496
- streaming: field({
411
+ reasoning: field({
497
412
  type: 'string',
498
- title: '思考图标',
499
- description: '思考时的图标',
500
- icon: 'i-lucide-chevron-down',
501
- default: 'i-lucide-chevron-down'
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-trash-2',
508
- default: 'i-lucide-trash-2'
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-x',
515
- default: 'i-lucide-x'
429
+ icon: 'i-lucide-panel-right-close',
430
+ default: 'i-lucide-panel-right-close'
516
431
  }),
517
432
  providers: field({
518
433
  type: 'object',