@movk/nuxt-docs 1.5.2 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) 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 +10 -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/composables/useToolCall.ts +9 -0
  26. package/app/layouts/docs.vue +2 -15
  27. package/app/pages/docs/[...slug].vue +1 -1
  28. package/app/utils/shiki-transformer-icon-highlight.ts +1 -1
  29. package/app/utils/unicode.ts +12 -0
  30. package/modules/ai-chat/index.ts +90 -0
  31. package/modules/ai-chat/runtime/components/AiChat.vue +22 -0
  32. package/modules/ai-chat/runtime/components/AiChatFloatingInput.vue +85 -0
  33. package/modules/ai-chat/runtime/components/AiChatModelSelect.vue +24 -0
  34. package/modules/ai-chat/runtime/components/AiChatPreStream.vue +58 -0
  35. package/modules/ai-chat/runtime/components/AiChatReasoning.vue +49 -0
  36. package/modules/ai-chat/runtime/components/AiChatSlideover.vue +255 -0
  37. package/modules/ai-chat/runtime/components/AiChatSlideoverFaq.vue +41 -0
  38. package/modules/ai-chat/runtime/components/AiChatToolCall.vue +31 -0
  39. package/modules/ai-chat/runtime/composables/useAIChat.ts +45 -0
  40. package/modules/ai-chat/runtime/composables/useModels.ts +58 -0
  41. package/modules/ai-chat/runtime/server/api/search.ts +84 -0
  42. package/modules/ai-chat/runtime/server/utils/docs_agent.ts +49 -0
  43. package/modules/ai-chat/runtime/server/utils/getModel.ts +25 -0
  44. package/modules/css.ts +2 -0
  45. package/nuxt.config.ts +27 -39
  46. package/package.json +16 -7
  47. package/server/mcp/tools/get-page.ts +60 -0
  48. package/server/mcp/tools/list-pages.ts +49 -0
  49. package/app/components/AdsCarbon.vue +0 -3
  50. package/app/components/Footer.vue +0 -32
  51. package/modules/llms.ts +0 -27
@@ -0,0 +1,22 @@
1
+ <script setup lang="ts">
2
+ const route = useRoute()
3
+ </script>
4
+
5
+ <template>
6
+ <USeparator :icon="route.path === '/' ? undefined : 'i-simple-icons-nuxtdotjs'" class="h-px" />
7
+
8
+ <UFooter
9
+ :ui="{
10
+ left: 'text-sm text-muted',
11
+ root: 'border-t border-default'
12
+ }"
13
+ >
14
+ <template #left>
15
+ <FooterLeft />
16
+ </template>
17
+
18
+ <template #right>
19
+ <FooterRight />
20
+ </template>
21
+ </UFooter>
22
+ </template>
@@ -0,0 +1,7 @@
1
+ <script lang="ts" setup>
2
+ const { footer } = useAppConfig()
3
+ </script>
4
+
5
+ <template>
6
+ <MDC v-if="footer?.credits" :value="footer.credits" unwrap="p" />
7
+ </template>
@@ -0,0 +1,14 @@
1
+ <script lang="ts" setup>
2
+ const { footer } = useAppConfig()
3
+ </script>
4
+
5
+ <template>
6
+ <UTooltip
7
+ v-for="(link, count) in footer.socials"
8
+ :key="count"
9
+ :text="link.label || (link as any)['aria-label']"
10
+ class="hidden lg:flex"
11
+ >
12
+ <UButton v-bind="{ color: 'neutral', variant: 'ghost', ...link }" />
13
+ </UTooltip>
14
+ </template>
@@ -2,7 +2,6 @@
2
2
  import type { ButtonProps } from '@nuxt/ui'
3
3
 
4
4
  const route = useRoute()
5
- const { desktopLinks } = useHeader()
6
5
  const { header, github } = useAppConfig()
7
6
 
8
7
  const links = computed<ButtonProps[]>(() => github && github.url
@@ -24,9 +23,11 @@ const links = computed<ButtonProps[]>(() => github && github.url
24
23
  <HeaderLogo />
25
24
  </template>
26
25
 
27
- <UNavigationMenu :items="desktopLinks" variant="link" />
26
+ <HeaderCenter />
28
27
 
29
28
  <template #right>
29
+ <HeaderCTA />
30
+
30
31
  <ThemePicker />
31
32
 
32
33
  <UTooltip text="Search" :kbds="['meta', 'K']">
@@ -50,11 +51,7 @@ const links = computed<ButtonProps[]>(() => github && github.url
50
51
  </template>
51
52
 
52
53
  <template #toggle="{ open, toggle, ui }">
53
- <HeaderToggleButton
54
- :open="open"
55
- :class="ui.toggle({ toggleSide: 'right' })"
56
- @click="toggle"
57
- />
54
+ <HeaderToggleButton :open="open" :class="ui.toggle({ toggleSide: 'right' })" @click="toggle" />
58
55
  </template>
59
56
 
60
57
  <template #body>
@@ -0,0 +1,18 @@
1
+ <script lang="ts" setup>
2
+ const { aiChat } = useRuntimeConfig().public
3
+ const route = useRoute()
4
+ </script>
5
+
6
+ <template>
7
+ <div v-if="aiChat.enable" class="hidden md:block">
8
+ <UButton
9
+ v-if="route.path === '/'"
10
+ to="/docs"
11
+ label="Get Started"
12
+ variant="ghost"
13
+ trailing
14
+ icon="i-lucide-arrow-right"
15
+ />
16
+ <AiChat v-else />
17
+ </div>
18
+ </template>
@@ -0,0 +1,7 @@
1
+ <script lang="ts" setup>
2
+ const { desktopLinks } = useHeader()
3
+ </script>
4
+
5
+ <template>
6
+ <UNavigationMenu :items="desktopLinks" variant="link" />
7
+ </template>
@@ -1,12 +1,8 @@
1
1
  <script setup lang="ts">
2
- import { omit } from '@movk/core'
3
- import colors from 'tailwindcss/colors'
4
2
  import { useClipboard } from '@vueuse/core'
5
- import { themeIcons } from '../../utils/theme'
6
3
 
7
4
  const appConfig = useAppConfig()
8
5
  const colorMode = useColorMode()
9
- const site = useSiteConfig()
10
6
 
11
7
  const { track } = useAnalytics()
12
8
 
@@ -21,199 +17,26 @@ watch(open, (isOpen) => {
21
17
  const { copy: copyCSS, copied: copiedCSS } = useClipboard()
22
18
  const { copy: copyAppConfig, copied: copiedAppConfig } = useClipboard()
23
19
 
24
- const neutralColors = ['slate', 'gray', 'zinc', 'neutral', 'stone']
25
- const neutral = computed({
26
- get() {
27
- return appConfig.ui.colors.neutral
28
- },
29
- set(option) {
30
- appConfig.ui.colors.neutral = option
31
- window.localStorage.setItem(`${site.name}-ui-neutral`, appConfig.ui.colors.neutral)
32
- if (appConfig.vercelAnalytics?.debug) track('Theme Changed', { setting: 'neutral', value: option })
33
- }
34
- })
35
-
36
- const colorsToOmit = ['inherit', 'current', 'transparent', 'black', 'white', ...neutralColors]
37
- const primaryColors = Object.keys(omit(colors, colorsToOmit as any))
38
- const primary = computed({
39
- get() {
40
- return appConfig.ui.colors.primary
41
- },
42
- set(option) {
43
- appConfig.ui.colors.primary = option
44
- window.localStorage.setItem(`${site.name}-ui-primary`, appConfig.ui.colors.primary)
45
- setBlackAsPrimary(false)
46
- if (appConfig.vercelAnalytics?.debug) track('Theme Changed', { setting: 'primary', value: option })
47
- }
48
- })
49
-
50
- const radiuses = [0, 0.125, 0.25, 0.375, 0.5]
51
- const radius = computed({
52
- get() {
53
- return appConfig.theme.radius
54
- },
55
- set(option) {
56
- appConfig.theme.radius = option
57
- window.localStorage.setItem(`${site.name}-ui-radius`, String(appConfig.theme.radius))
58
- if (appConfig.vercelAnalytics?.debug) track('Theme Changed', { setting: 'radius', value: option })
59
- }
60
- })
61
-
62
- const modes = [
63
- { label: 'light', icon: 'i-lucide-sun' },
64
- { label: 'dark', icon: 'i-lucide-moon' },
65
- { label: 'system', icon: 'i-lucide-monitor' }
66
- ]
67
- const mode = computed({
68
- get() {
69
- return colorMode.value
70
- },
71
- set(option) {
72
- colorMode.preference = option
73
- if (appConfig.vercelAnalytics?.debug) track('Theme Changed', { setting: 'color mode', value: option })
74
- }
75
- })
76
-
77
- function setBlackAsPrimary(value: boolean) {
78
- appConfig.theme.blackAsPrimary = value
79
- window.localStorage.setItem(`${site.name}-ui-black-as-primary`, String(value))
80
- if (appConfig.vercelAnalytics?.debug) track('Theme Changed', { setting: 'black as primary', value })
81
- }
82
-
83
- const fonts = ['Public Sans', 'DM Sans', 'Geist', 'Inter', 'Poppins', 'Outfit', 'Raleway']
84
- const font = computed({
85
- get() {
86
- return appConfig.theme.font
87
- },
88
- set(option) {
89
- appConfig.theme.font = option
90
- window.localStorage.setItem(`${site.name}-ui-font`, appConfig.theme.font)
91
- if (appConfig.vercelAnalytics?.debug) track('Theme Changed', { setting: 'font', value: option })
92
- }
93
- })
94
-
95
- const icons = [{
96
- label: 'Lucide',
97
- icon: 'i-lucide-feather',
98
- value: 'lucide'
99
- }, {
100
- label: 'Phosphor',
101
- icon: 'i-ph-phosphor-logo',
102
- value: 'phosphor'
103
- }, {
104
- label: 'Tabler',
105
- icon: 'i-tabler-brand-tabler',
106
- value: 'tabler'
107
- }]
108
- const icon = computed({
109
- get() {
110
- return appConfig.theme.icons
111
- },
112
- set(option) {
113
- appConfig.theme.icons = option
114
- appConfig.ui.icons = themeIcons[option as keyof typeof themeIcons] as any
115
- window.localStorage.setItem(`${site.name}-ui-icons`, appConfig.theme.icons)
116
- if (appConfig.vercelAnalytics?.debug) track('Theme Changed', { setting: 'icons', value: option })
117
- }
118
- })
119
-
120
- const hasCSSChanges = computed(() => {
121
- return appConfig.theme.radius !== 0.25
122
- || appConfig.theme.blackAsPrimary
123
- || appConfig.theme.font !== 'Public Sans'
124
- })
125
-
126
- const hasAppConfigChanges = computed(() => {
127
- return appConfig.ui.colors.primary !== 'green'
128
- || appConfig.ui.colors.neutral !== 'slate'
129
- || appConfig.theme.icons !== 'lucide'
130
- })
131
-
132
- function exportCSS() {
133
- if (appConfig.vercelAnalytics?.debug) track('Theme Exported', { type: 'css' })
134
-
135
- const lines = [
136
- '@import "tailwindcss";',
137
- '@import "@nuxt/ui";'
138
- ]
139
-
140
- if (appConfig.theme.font !== 'Public Sans') {
141
- lines.push('', '@theme {', ` --font-sans: '${appConfig.theme.font}', sans-serif;`, '}')
142
- }
143
-
144
- const rootLines: string[] = []
145
- if (appConfig.theme.radius !== 0.25) {
146
- rootLines.push(` --ui-radius: ${appConfig.theme.radius}rem;`)
147
- }
148
- if (appConfig.theme.blackAsPrimary) {
149
- rootLines.push(' --ui-primary: black;')
150
- }
151
-
152
- if (rootLines.length) {
153
- lines.push('', ':root {', ...rootLines, '}')
154
- }
155
-
156
- if (appConfig.theme.blackAsPrimary) {
157
- lines.push('', '.dark {', ' --ui-primary: white;', '}')
158
- }
159
-
160
- copyCSS(lines.join('\n'))
161
- }
162
-
163
- function exportAppConfig() {
164
- if (appConfig.vercelAnalytics?.debug) track('Theme Exported', { type: 'appConfig' })
165
-
166
- const config: Record<string, any> = {}
167
-
168
- if (appConfig.ui.colors.primary !== 'green' || appConfig.ui.colors.neutral !== 'slate') {
169
- config.ui = { colors: {} }
170
- if (appConfig.ui.colors.primary !== 'green') {
171
- config.ui.colors.primary = appConfig.ui.colors.primary
172
- }
173
- if (appConfig.ui.colors.neutral !== 'slate') {
174
- config.ui.colors.neutral = appConfig.ui.colors.neutral
175
- }
176
- }
177
-
178
- if (appConfig.theme.icons !== 'lucide') {
179
- const iconSet = appConfig.theme.icons
180
- const icons = themeIcons[iconSet as keyof typeof themeIcons]
181
- config.ui = config.ui || {}
182
- config.ui.icons = icons
183
- }
184
-
185
- const configString = JSON.stringify(config, null, 2)
186
- .replace(/"([^"]+)":/g, '$1:')
187
- .replace(/"/g, '\'')
188
-
189
- const output = `export default defineAppConfig(${configString})`
190
-
191
- copyAppConfig(output)
192
- }
193
-
194
- function resetTheme() {
195
- if (appConfig.vercelAnalytics?.debug) track('Theme Reset')
196
-
197
- // Reset without triggering individual tracking events
198
- appConfig.ui.colors.primary = 'green'
199
- window.localStorage.removeItem(`${site.name}-ui-primary`)
200
-
201
- appConfig.ui.colors.neutral = 'slate'
202
- window.localStorage.removeItem(`${site.name}-ui-neutral`)
203
-
204
- appConfig.theme.radius = 0.25
205
- window.localStorage.removeItem(`${site.name}-ui-radius`)
206
-
207
- appConfig.theme.font = 'Public Sans'
208
- window.localStorage.removeItem(`${site.name}-ui-font`)
209
-
210
- appConfig.theme.icons = 'lucide'
211
- appConfig.ui.icons = themeIcons.lucide as any
212
- window.localStorage.removeItem(`${site.name}-ui-icons`)
213
-
214
- appConfig.theme.blackAsPrimary = false
215
- window.localStorage.removeItem(`${site.name}-ui-black-as-primary`)
216
- }
20
+ const {
21
+ neutralColors,
22
+ neutral,
23
+ primaryColors,
24
+ primary,
25
+ setBlackAsPrimary,
26
+ radiuses,
27
+ radius,
28
+ fonts,
29
+ font,
30
+ icon,
31
+ icons,
32
+ modes,
33
+ mode,
34
+ hasCSSChanges,
35
+ hasAppConfigChanges,
36
+ exportCSS,
37
+ exportAppConfig,
38
+ resetTheme
39
+ } = useTheme()
217
40
  </script>
218
41
 
219
42
  <template>
@@ -418,8 +241,8 @@ function resetTheme() {
418
241
  size="sm"
419
242
  label="main.css"
420
243
  class="flex-1 text-[11px]"
421
- :icon="copiedCSS ? 'i-lucide-copy-check' : 'i-lucide-copy'"
422
- @click="exportCSS"
244
+ :icon="copiedCSS ? appConfig.ui.icons.copyCheck : appConfig.ui.icons.copy"
245
+ @click="copyCSS(exportCSS())"
423
246
  />
424
247
  <UButton
425
248
  v-if="hasAppConfigChanges"
@@ -427,9 +250,9 @@ function resetTheme() {
427
250
  variant="soft"
428
251
  size="sm"
429
252
  label="app.config.ts"
430
- :icon="copiedAppConfig ? 'i-lucide-copy-check' : 'i-lucide-copy'"
253
+ :icon="copiedAppConfig ? appConfig.ui.icons.copyCheck : appConfig.ui.icons.copy"
431
254
  class="flex-1 text-[11px]"
432
- @click="exportAppConfig"
255
+ @click="copyAppConfig(exportAppConfig())"
433
256
  />
434
257
  <UTooltip text="Reset theme">
435
258
  <UButton
@@ -0,0 +1,21 @@
1
+ export interface FaqItem {
2
+ category: string
3
+ items: string[]
4
+ }
5
+
6
+ export function useFaq() {
7
+ const faqQuestions: FaqItem[] = [
8
+ {
9
+ category: 'MCP 工具使用',
10
+ items: [
11
+ '如何查询所有可用的文档页面?',
12
+ '如何获取特定文档页面的完整内容?',
13
+ '什么时候应该使用 list-pages 而不是 get-page?'
14
+ ]
15
+ }
16
+ ]
17
+
18
+ return {
19
+ faqQuestions
20
+ }
21
+ }
@@ -0,0 +1,22 @@
1
+ import { createHighlighter } from 'shiki'
2
+ import type { HighlighterGeneric } from 'shiki'
3
+ import { createJavaScriptRegexEngine } from 'shiki/engine-javascript.mjs'
4
+
5
+ let highlighter: HighlighterGeneric<any, any> | null = null
6
+
7
+ let promise: Promise<HighlighterGeneric<any, any>> | null = null
8
+
9
+ export const useHighlighter = async () => {
10
+ if (!promise) {
11
+ promise = createHighlighter({
12
+ langs: ['vue', 'js', 'ts', 'css', 'html', 'json', 'yaml', 'markdown', 'bash'],
13
+ themes: ['material-theme-palenight', 'material-theme-lighter'],
14
+ engine: createJavaScriptRegexEngine()
15
+ })
16
+ }
17
+ if (!highlighter) {
18
+ highlighter = await promise
19
+ }
20
+
21
+ return highlighter
22
+ }
@@ -0,0 +1,223 @@
1
+ import { themeIcons } from '../utils/theme'
2
+ import { omit } from '@movk/core'
3
+ import colors from 'tailwindcss/colors'
4
+
5
+ export function useTheme() {
6
+ const appConfig = useAppConfig()
7
+ const colorMode = useColorMode()
8
+ const site = useSiteConfig()
9
+ const { track } = useAnalytics()
10
+
11
+ const neutralColors = ['slate', 'gray', 'zinc', 'neutral', 'stone']
12
+ const neutral = computed({
13
+ get() {
14
+ return appConfig.ui.colors.neutral
15
+ },
16
+ set(option) {
17
+ appConfig.ui.colors.neutral = option
18
+ window.localStorage.setItem(`${site.name}-ui-neutral`, appConfig.ui.colors.neutral)
19
+ if (appConfig.vercelAnalytics?.debug) track('Theme Changed', { setting: 'neutral', value: option })
20
+ }
21
+ })
22
+
23
+ const colorsToOmit = ['inherit', 'current', 'transparent', 'black', 'white', ...neutralColors]
24
+ const primaryColors = Object.keys(omit(colors, colorsToOmit as any))
25
+ const primary = computed({
26
+ get() {
27
+ return appConfig.ui.colors.primary
28
+ },
29
+ set(option) {
30
+ appConfig.ui.colors.primary = option
31
+ window.localStorage.setItem(`${site.name}-ui-primary`, appConfig.ui.colors.primary)
32
+ setBlackAsPrimary(false)
33
+ if (appConfig.vercelAnalytics?.debug) track('Theme Changed', { setting: 'primary', value: option })
34
+ }
35
+ })
36
+
37
+ const radiuses = [0, 0.125, 0.25, 0.375, 0.5]
38
+ const radius = computed({
39
+ get() {
40
+ return appConfig.theme.radius
41
+ },
42
+ set(option) {
43
+ appConfig.theme.radius = option
44
+ window.localStorage.setItem(`${site.name}-ui-radius`, String(appConfig.theme.radius))
45
+ if (appConfig.vercelAnalytics?.debug) track('Theme Changed', { setting: 'radius', value: option })
46
+ }
47
+ })
48
+
49
+ const fonts = ['Public Sans', 'DM Sans', 'Geist', 'Inter', 'Poppins', 'Outfit', 'Raleway']
50
+ const font = computed({
51
+ get() {
52
+ return appConfig.theme.font
53
+ },
54
+ set(option) {
55
+ appConfig.theme.font = option
56
+ window.localStorage.setItem(`${site.name}-ui-font`, appConfig.theme.font)
57
+ if (appConfig.vercelAnalytics?.debug) track('Theme Changed', { setting: 'font', value: option })
58
+ }
59
+ })
60
+
61
+ const icons = [{
62
+ label: 'Lucide',
63
+ icon: 'i-lucide-feather',
64
+ value: 'lucide'
65
+ }, {
66
+ label: 'Phosphor',
67
+ icon: 'i-ph-phosphor-logo',
68
+ value: 'phosphor'
69
+ }, {
70
+ label: 'Tabler',
71
+ icon: 'i-tabler-brand-tabler',
72
+ value: 'tabler'
73
+ }]
74
+ const icon = computed({
75
+ get() {
76
+ return appConfig.theme.icons
77
+ },
78
+ set(option) {
79
+ appConfig.theme.icons = option
80
+ appConfig.ui.icons = themeIcons[option as keyof typeof themeIcons] as any
81
+ window.localStorage.setItem(`${site.name}-ui-icons`, appConfig.theme.icons)
82
+ if (appConfig.vercelAnalytics?.debug) track('Theme Changed', { setting: 'icons', value: option })
83
+ }
84
+ })
85
+
86
+ const modes = [
87
+ { label: 'light', icon: appConfig.ui.icons.light },
88
+ { label: 'dark', icon: appConfig.ui.icons.dark },
89
+ { label: 'system', icon: appConfig.ui.icons.system }
90
+ ]
91
+ const mode = computed({
92
+ get() {
93
+ return colorMode.value
94
+ },
95
+ set(option) {
96
+ colorMode.preference = option
97
+ if (appConfig.vercelAnalytics?.debug) track('Theme Changed', { setting: 'color mode', value: option })
98
+ }
99
+ })
100
+
101
+ function setBlackAsPrimary(value: boolean) {
102
+ appConfig.theme.blackAsPrimary = value
103
+ window.localStorage.setItem(`${site.name}-ui-black-as-primary`, String(value))
104
+ if (appConfig.vercelAnalytics?.debug && value) track('Theme Changed', { setting: 'black as primary', value })
105
+ }
106
+
107
+ const hasCSSChanges = computed(() => {
108
+ return appConfig.theme.radius !== 0.25
109
+ || appConfig.theme.blackAsPrimary
110
+ || appConfig.theme.font !== 'Public Sans'
111
+ })
112
+
113
+ const hasAppConfigChanges = computed(() => {
114
+ return appConfig.ui.colors.primary !== 'green'
115
+ || appConfig.ui.colors.neutral !== 'slate'
116
+ || appConfig.theme.icons !== 'lucide'
117
+ })
118
+
119
+ function exportCSS(): string {
120
+ if (appConfig.vercelAnalytics?.debug) track('Theme Exported', { type: 'css' })
121
+
122
+ const lines = [
123
+ '@import "tailwindcss";',
124
+ '@import "@nuxt/ui";'
125
+ ]
126
+
127
+ if (appConfig.theme.font !== 'Public Sans') {
128
+ lines.push('', '@theme {', ` --font-sans: '${appConfig.theme.font}', sans-serif;`, '}')
129
+ }
130
+
131
+ const rootLines: string[] = []
132
+ if (appConfig.theme.radius !== 0.25) {
133
+ rootLines.push(` --ui-radius: ${appConfig.theme.radius}rem;`)
134
+ }
135
+ if (appConfig.theme.blackAsPrimary) {
136
+ rootLines.push(' --ui-primary: black;')
137
+ }
138
+
139
+ if (rootLines.length) {
140
+ lines.push('', ':root {', ...rootLines, '}')
141
+ }
142
+
143
+ if (appConfig.theme.blackAsPrimary) {
144
+ lines.push('', '.dark {', ' --ui-primary: white;', '}')
145
+ }
146
+
147
+ return lines.join('\n')
148
+ }
149
+
150
+ function exportAppConfig(): string {
151
+ if (appConfig.vercelAnalytics?.debug) track('Theme Exported', { type: 'appConfig' })
152
+
153
+ const config: Record<string, any> = {}
154
+
155
+ if (appConfig.ui.colors.primary !== 'green' || appConfig.ui.colors.neutral !== 'slate') {
156
+ config.ui = { colors: {} }
157
+ if (appConfig.ui.colors.primary !== 'green') {
158
+ config.ui.colors.primary = appConfig.ui.colors.primary
159
+ }
160
+ if (appConfig.ui.colors.neutral !== 'slate') {
161
+ config.ui.colors.neutral = appConfig.ui.colors.neutral
162
+ }
163
+ }
164
+
165
+ if (appConfig.theme.icons !== 'lucide') {
166
+ const iconSet = appConfig.theme.icons
167
+ const icons = themeIcons[iconSet as keyof typeof themeIcons]
168
+ config.ui = config.ui || {}
169
+ config.ui.icons = icons
170
+ }
171
+
172
+ const configString = JSON.stringify(config, null, 2)
173
+ .replace(/"([^"]+)":/g, '$1:')
174
+ .replace(/"/g, '\'')
175
+
176
+ return `export default defineAppConfig(${configString})`
177
+ }
178
+
179
+ function resetTheme() {
180
+ if (appConfig.vercelAnalytics?.debug) track('Theme Reset')
181
+
182
+ // Reset without triggering individual tracking events
183
+ appConfig.ui.colors.primary = 'green'
184
+ window.localStorage.removeItem(`${site.name}-ui-primary`)
185
+
186
+ appConfig.ui.colors.neutral = 'slate'
187
+ window.localStorage.removeItem(`${site.name}-ui-neutral`)
188
+
189
+ appConfig.theme.radius = 0.25
190
+ window.localStorage.removeItem(`${site.name}-ui-radius`)
191
+
192
+ appConfig.theme.font = 'Public Sans'
193
+ window.localStorage.removeItem(`${site.name}-ui-font`)
194
+
195
+ appConfig.theme.icons = 'lucide'
196
+ appConfig.ui.icons = themeIcons.lucide as any
197
+ window.localStorage.removeItem(`${site.name}-ui-icons`)
198
+
199
+ appConfig.theme.blackAsPrimary = false
200
+ window.localStorage.removeItem(`${site.name}-ui-black-as-primary`)
201
+ }
202
+
203
+ return {
204
+ neutralColors,
205
+ neutral,
206
+ primaryColors,
207
+ primary,
208
+ setBlackAsPrimary,
209
+ radiuses,
210
+ radius,
211
+ fonts,
212
+ font,
213
+ icon,
214
+ icons,
215
+ modes,
216
+ mode,
217
+ hasCSSChanges,
218
+ hasAppConfigChanges,
219
+ exportCSS,
220
+ exportAppConfig,
221
+ resetTheme
222
+ }
223
+ }
@@ -0,0 +1,9 @@
1
+ export function useToolCall() {
2
+ const tools: Record<string, string | ((args: any) => string)> = {
3
+ 'list-pages': '列出所有文档页面',
4
+ 'get-page': (args: any) => `检索 ${args?.path || '页面'}`
5
+ }
6
+ return {
7
+ tools
8
+ }
9
+ }
@@ -1,13 +1,3 @@
1
- <script setup lang="ts">
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
1
  <template>
12
2
  <UMain class="relative">
13
3
  <HeroBackground />
@@ -16,11 +6,8 @@ const { navigationByCategory } = useNavigation(navigation!)
16
6
  <UPage>
17
7
  <template #left>
18
8
  <UPageAside>
19
- <UContentNavigation
20
- :key="route.path"
21
- :navigation="navigationByCategory"
22
- highlight
23
- />
9
+ <DocsAsideLeftTop />
10
+ <DocsAsideLeftBody />
24
11
  </UPageAside>
25
12
  </template>
26
13
 
@@ -151,7 +151,7 @@ defineOgImageComponent('Nuxt', {
151
151
 
152
152
  <USeparator v-if="communityLinks?.length" type="dashed" />
153
153
 
154
- <AdsCarbon />
154
+ <DocsAsideRightBottom />
155
155
  </div>
156
156
  </template>
157
157
  </UContentToc>
@@ -76,7 +76,7 @@ export function transformerIconHighlight(options: TransformerIconHighlightOption
76
76
  tagName: 'i',
77
77
  properties: {
78
78
  class: 'shiki-icon-highlight',
79
- style: `--shiki-icon-url: url('${iconUrl}')`
79
+ style: `--shiki-icon-url: url(${iconUrl})`
80
80
  },
81
81
  children: []
82
82
  }