@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
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { themeIcons } from '../utils/theme'
|
|
2
|
-
import { omit } from '@movk/core'
|
|
2
|
+
import { omit, kebabCase } from '@movk/core'
|
|
3
|
+
import { useLocalStorage } from '@vueuse/core'
|
|
3
4
|
import colors from 'tailwindcss/colors'
|
|
4
5
|
|
|
5
6
|
export function useTheme() {
|
|
6
7
|
const appConfig = useAppConfig()
|
|
7
8
|
const colorMode = useColorMode()
|
|
8
9
|
const site = useSiteConfig()
|
|
9
|
-
const { track } = useAnalytics()
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const radius = useLocalStorage(`${site.name}-ui-radius`, 0.25)
|
|
12
|
+
const font = useLocalStorage(`${site.name}-ui-font`, 'Public Sans')
|
|
13
|
+
const _iconSet = useLocalStorage(`${site.name}-ui-icons`, 'lucide')
|
|
14
|
+
const blackAsPrimary = useLocalStorage(`${site.name}-ui-black-as-primary`, false)
|
|
15
|
+
|
|
16
|
+
const neutralColors = ['slate', 'gray', 'zinc', 'neutral', 'stone', 'taupe', 'mauve', 'mist', 'olive']
|
|
12
17
|
const neutral = computed({
|
|
13
18
|
get() {
|
|
14
19
|
return appConfig.ui.colors.neutral
|
|
@@ -16,7 +21,6 @@ export function useTheme() {
|
|
|
16
21
|
set(option) {
|
|
17
22
|
appConfig.ui.colors.neutral = option
|
|
18
23
|
window.localStorage.setItem(`${site.name}-ui-neutral`, appConfig.ui.colors.neutral)
|
|
19
|
-
if (appConfig.vercelAnalytics?.debug) track('Theme Changed', { setting: 'neutral', value: option })
|
|
20
24
|
}
|
|
21
25
|
})
|
|
22
26
|
|
|
@@ -29,36 +33,12 @@ export function useTheme() {
|
|
|
29
33
|
set(option) {
|
|
30
34
|
appConfig.ui.colors.primary = option
|
|
31
35
|
window.localStorage.setItem(`${site.name}-ui-primary`, appConfig.ui.colors.primary)
|
|
32
|
-
|
|
33
|
-
if (appConfig.vercelAnalytics?.debug) track('Theme Changed', { setting: 'primary', value: option })
|
|
36
|
+
blackAsPrimary.value = false
|
|
34
37
|
}
|
|
35
38
|
})
|
|
36
39
|
|
|
37
40
|
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
41
|
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
|
-
if (appConfig.theme.font) {
|
|
57
|
-
window.localStorage.setItem(`${site.name}-ui-font`, appConfig.theme.font)
|
|
58
|
-
}
|
|
59
|
-
if (appConfig.vercelAnalytics?.debug) track('Theme Changed', { setting: 'font', value: option })
|
|
60
|
-
}
|
|
61
|
-
})
|
|
62
42
|
|
|
63
43
|
const icons = [{
|
|
64
44
|
label: 'Lucide',
|
|
@@ -75,68 +55,75 @@ export function useTheme() {
|
|
|
75
55
|
}]
|
|
76
56
|
const icon = computed({
|
|
77
57
|
get() {
|
|
78
|
-
return
|
|
58
|
+
return _iconSet.value
|
|
79
59
|
},
|
|
80
60
|
set(option) {
|
|
81
|
-
|
|
61
|
+
_iconSet.value = option
|
|
82
62
|
appConfig.ui.icons = themeIcons[option as keyof typeof themeIcons] as any
|
|
83
|
-
if (appConfig.theme.icons) {
|
|
84
|
-
window.localStorage.setItem(`${site.name}-ui-icons`, appConfig.theme.icons)
|
|
85
|
-
}
|
|
86
|
-
if (appConfig.vercelAnalytics?.debug) track('Theme Changed', { setting: 'icons', value: option })
|
|
87
63
|
}
|
|
88
64
|
})
|
|
89
65
|
|
|
90
|
-
const modes = [
|
|
66
|
+
const modes = computed(() => [
|
|
91
67
|
{ label: 'light', icon: appConfig.ui.icons.light },
|
|
92
68
|
{ label: 'dark', icon: appConfig.ui.icons.dark },
|
|
93
69
|
{ label: 'system', icon: appConfig.ui.icons.system }
|
|
94
|
-
]
|
|
70
|
+
])
|
|
95
71
|
const mode = computed({
|
|
96
72
|
get() {
|
|
97
73
|
return colorMode.value
|
|
98
74
|
},
|
|
99
75
|
set(option) {
|
|
100
76
|
colorMode.preference = option
|
|
101
|
-
if (appConfig.vercelAnalytics?.debug) track('Theme Changed', { setting: 'color mode', value: option })
|
|
102
77
|
}
|
|
103
78
|
})
|
|
104
79
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
80
|
+
const radiusStyle = computed(() => `:root { --ui-radius: ${radius.value}rem; }`)
|
|
81
|
+
const blackAsPrimaryStyle = computed(() => blackAsPrimary.value ? `:root { --ui-primary: black; } .dark { --ui-primary: white; }` : ':root {}')
|
|
82
|
+
const fontStyle = computed(() => `:root { --font-sans: '${font.value}', sans-serif; }`)
|
|
83
|
+
|
|
84
|
+
const link = computed(() => {
|
|
85
|
+
const name = font.value
|
|
86
|
+
if (name === 'Public Sans') return []
|
|
87
|
+
return [{
|
|
88
|
+
rel: 'stylesheet' as const,
|
|
89
|
+
href: `https://fonts.googleapis.com/css2?family=${encodeURIComponent(name)}:wght@400;500;600;700&display=swap`,
|
|
90
|
+
id: `font-${kebabCase(name)}`
|
|
91
|
+
}]
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const style = [
|
|
95
|
+
{ innerHTML: radiusStyle, id: `${site.name}-ui-radius`, tagPriority: -2 },
|
|
96
|
+
{ innerHTML: blackAsPrimaryStyle, id: `${site.name}-ui-black-as-primary`, tagPriority: -2 },
|
|
97
|
+
{ innerHTML: fontStyle, id: `${site.name}-ui-font`, tagPriority: -2 }
|
|
98
|
+
]
|
|
110
99
|
|
|
111
100
|
const hasCSSChanges = computed(() => {
|
|
112
|
-
return
|
|
113
|
-
||
|
|
114
|
-
||
|
|
101
|
+
return radius.value !== 0.25
|
|
102
|
+
|| blackAsPrimary.value
|
|
103
|
+
|| font.value !== 'Public Sans'
|
|
115
104
|
})
|
|
116
105
|
|
|
117
106
|
const hasAppConfigChanges = computed(() => {
|
|
118
107
|
return appConfig.ui.colors.primary !== 'green'
|
|
119
108
|
|| appConfig.ui.colors.neutral !== 'slate'
|
|
120
|
-
||
|
|
109
|
+
|| _iconSet.value !== 'lucide'
|
|
121
110
|
})
|
|
122
111
|
|
|
123
112
|
function exportCSS(): string {
|
|
124
|
-
if (appConfig.vercelAnalytics?.debug) track('Theme Exported', { type: 'css' })
|
|
125
|
-
|
|
126
113
|
const lines = [
|
|
127
114
|
'@import "tailwindcss";',
|
|
128
115
|
'@import "@nuxt/ui";'
|
|
129
116
|
]
|
|
130
117
|
|
|
131
|
-
if (
|
|
132
|
-
lines.push('', '@theme {', ` --font-sans: '${
|
|
118
|
+
if (font.value !== 'Public Sans') {
|
|
119
|
+
lines.push('', '@theme {', ` --font-sans: '${font.value}', sans-serif;`, '}')
|
|
133
120
|
}
|
|
134
121
|
|
|
135
122
|
const rootLines: string[] = []
|
|
136
|
-
if (
|
|
137
|
-
rootLines.push(` --ui-radius: ${
|
|
123
|
+
if (radius.value !== 0.25) {
|
|
124
|
+
rootLines.push(` --ui-radius: ${radius.value}rem;`)
|
|
138
125
|
}
|
|
139
|
-
if (
|
|
126
|
+
if (blackAsPrimary.value) {
|
|
140
127
|
rootLines.push(' --ui-primary: black;')
|
|
141
128
|
}
|
|
142
129
|
|
|
@@ -144,33 +131,30 @@ export function useTheme() {
|
|
|
144
131
|
lines.push('', ':root {', ...rootLines, '}')
|
|
145
132
|
}
|
|
146
133
|
|
|
147
|
-
|
|
148
|
-
|
|
134
|
+
const darkLines: string[] = []
|
|
135
|
+
if (blackAsPrimary.value) {
|
|
136
|
+
darkLines.push(' --ui-primary: white;')
|
|
149
137
|
}
|
|
150
138
|
|
|
139
|
+
if (darkLines.length) {
|
|
140
|
+
lines.push('', '.dark {', ...darkLines, '}')
|
|
141
|
+
}
|
|
151
142
|
return lines.join('\n')
|
|
152
143
|
}
|
|
153
144
|
|
|
154
145
|
function exportAppConfig(): string {
|
|
155
|
-
if (appConfig.vercelAnalytics?.debug) track('Theme Exported', { type: 'appConfig' })
|
|
156
|
-
|
|
157
146
|
const config: Record<string, any> = {}
|
|
158
147
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
if (appConfig.ui.colors.neutral !== 'slate') {
|
|
165
|
-
config.ui.colors.neutral = appConfig.ui.colors.neutral
|
|
166
|
-
}
|
|
148
|
+
const defaultColors: Record<string, string> = { primary: 'green', neutral: 'slate', secondary: 'blue', success: 'green', info: 'blue', warning: 'yellow', error: 'red' }
|
|
149
|
+
const colorEntries = Object.entries(defaultColors).filter(([key, def]) => (appConfig.ui.colors as any)[key] !== def)
|
|
150
|
+
if (colorEntries.length) {
|
|
151
|
+
config.ui = { colors: Object.fromEntries(colorEntries.map(([key]) => [key, (appConfig.ui.colors as any)[key]])) }
|
|
167
152
|
}
|
|
168
153
|
|
|
169
|
-
if (
|
|
170
|
-
const
|
|
171
|
-
const icons = themeIcons[iconSet as keyof typeof themeIcons]
|
|
154
|
+
if (_iconSet.value !== 'lucide') {
|
|
155
|
+
const iconMapping = themeIcons[_iconSet.value as keyof typeof themeIcons]
|
|
172
156
|
config.ui = config.ui || {}
|
|
173
|
-
config.ui.icons =
|
|
157
|
+
config.ui.icons = iconMapping
|
|
174
158
|
}
|
|
175
159
|
|
|
176
160
|
const configString = JSON.stringify(config, null, 2)
|
|
@@ -181,35 +165,31 @@ export function useTheme() {
|
|
|
181
165
|
}
|
|
182
166
|
|
|
183
167
|
function resetTheme() {
|
|
184
|
-
if (appConfig.vercelAnalytics?.debug) track('Theme Reset')
|
|
185
|
-
|
|
186
|
-
// Reset without triggering individual tracking events
|
|
187
168
|
appConfig.ui.colors.primary = 'green'
|
|
188
169
|
window.localStorage.removeItem(`${site.name}-ui-primary`)
|
|
189
170
|
|
|
190
171
|
appConfig.ui.colors.neutral = 'slate'
|
|
191
172
|
window.localStorage.removeItem(`${site.name}-ui-neutral`)
|
|
192
173
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
appConfig.theme.font = 'Public Sans'
|
|
197
|
-
window.localStorage.removeItem(`${site.name}-ui-font`)
|
|
198
|
-
|
|
199
|
-
appConfig.theme.icons = 'lucide'
|
|
174
|
+
radius.value = 0.25
|
|
175
|
+
font.value = 'Public Sans'
|
|
176
|
+
_iconSet.value = 'lucide'
|
|
200
177
|
appConfig.ui.icons = themeIcons.lucide as any
|
|
201
|
-
|
|
178
|
+
blackAsPrimary.value = false
|
|
202
179
|
|
|
203
|
-
|
|
204
|
-
window.localStorage.removeItem(`${site.name}-ui-
|
|
180
|
+
window.localStorage.removeItem(`${site.name}-ui-ai-theme`)
|
|
181
|
+
window.localStorage.removeItem(`${site.name}-ui-custom-colors`)
|
|
182
|
+
window.localStorage.removeItem(`${site.name}-ui-css-variables`)
|
|
205
183
|
}
|
|
206
184
|
|
|
207
185
|
return {
|
|
186
|
+
style,
|
|
187
|
+
link,
|
|
208
188
|
neutralColors,
|
|
209
189
|
neutral,
|
|
210
190
|
primaryColors,
|
|
211
191
|
primary,
|
|
212
|
-
|
|
192
|
+
blackAsPrimary,
|
|
213
193
|
radiuses,
|
|
214
194
|
radius,
|
|
215
195
|
fonts,
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
'get-page': (args: any) => `检索 ${args?.path || '页面'}`,
|
|
5
|
-
'list-examples': '列出所有示例',
|
|
6
|
-
'get-example': (args: any) => `获取示例:${args?.exampleName || '示例'}`
|
|
7
|
-
}
|
|
1
|
+
import type { ToolState } from '#ai-chat/types'
|
|
2
|
+
|
|
3
|
+
export function useToolCall(_state: ToolState, _toolName: string, _input: Record<string, string | undefined>) {
|
|
8
4
|
return {
|
|
9
|
-
|
|
5
|
+
toolMessage: {} as Record<string, string>,
|
|
6
|
+
toolIcon: {} as Record<string, string>
|
|
10
7
|
}
|
|
11
8
|
}
|
package/app/error.vue
CHANGED
|
@@ -54,7 +54,7 @@ provide('navigation', rootNavigation)
|
|
|
54
54
|
<Footer v-if="$route.meta.footer !== false" />
|
|
55
55
|
|
|
56
56
|
<ClientOnly>
|
|
57
|
-
<
|
|
57
|
+
<UContentSearch :files="files" :navigation="navigation" />
|
|
58
58
|
</ClientOnly>
|
|
59
59
|
</div>
|
|
60
60
|
</UApp>
|
|
@@ -9,7 +9,8 @@ definePageMeta({
|
|
|
9
9
|
})
|
|
10
10
|
|
|
11
11
|
const route = useRoute()
|
|
12
|
-
const
|
|
12
|
+
const appConfig = useAppConfig()
|
|
13
|
+
const { toc, github } = appConfig
|
|
13
14
|
|
|
14
15
|
const { data: page } = await useAsyncData(`docs-${kebabCase(route.path)}`, () => queryCollection('docs').path(route.path).first())
|
|
15
16
|
|
|
@@ -18,7 +19,6 @@ if (!page.value) {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
|
|
21
|
-
const { shouldPushContent: shouldHideToc } = useAIChat()
|
|
22
22
|
|
|
23
23
|
const { data: surround } = await useAsyncData(`surround-${(kebabCase(route.path))}`, () => {
|
|
24
24
|
return queryCollectionItemSurroundings('docs', route.path, {
|
|
@@ -102,15 +102,15 @@ useSeoMeta({
|
|
|
102
102
|
ogDescription: description
|
|
103
103
|
})
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
defineOgImage('Docs', {
|
|
106
106
|
title,
|
|
107
107
|
description,
|
|
108
|
-
|
|
108
|
+
siteName: site.name
|
|
109
109
|
})
|
|
110
110
|
</script>
|
|
111
111
|
|
|
112
112
|
<template>
|
|
113
|
-
<UPage v-if="page"
|
|
113
|
+
<UPage v-if="page">
|
|
114
114
|
<UPageHeader :title="title">
|
|
115
115
|
<template #headline>
|
|
116
116
|
<UBreadcrumb :items="breadcrumb" />
|
|
@@ -158,7 +158,7 @@ defineOgImageComponent('Nuxt', {
|
|
|
158
158
|
<UContentSurround :surround="surround" />
|
|
159
159
|
</UPageBody>
|
|
160
160
|
|
|
161
|
-
<template v-if="page?.body?.toc?.links?.length
|
|
161
|
+
<template v-if="page?.body?.toc?.links?.length" #right>
|
|
162
162
|
<UContentToc
|
|
163
163
|
:title="toc?.title"
|
|
164
164
|
:links="page.body?.toc?.links"
|
package/app/plugins/theme.ts
CHANGED
|
@@ -7,84 +7,49 @@ export default defineNuxtPlugin({
|
|
|
7
7
|
const site = useSiteConfig()
|
|
8
8
|
|
|
9
9
|
if (import.meta.client) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if (color) {
|
|
13
|
-
appConfig.ui.colors[type] = color
|
|
14
|
-
}
|
|
15
|
-
}
|
|
10
|
+
const primary = localStorage.getItem(`${site.name}-ui-primary`)
|
|
11
|
+
if (primary) appConfig.ui.colors.primary = primary
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (radius) {
|
|
20
|
-
appConfig.theme.radius = Number.parseFloat(radius)
|
|
21
|
-
}
|
|
22
|
-
}
|
|
13
|
+
const neutral = localStorage.getItem(`${site.name}-ui-neutral`)
|
|
14
|
+
if (neutral) appConfig.ui.colors.neutral = neutral
|
|
23
15
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (blackAsPrimary) {
|
|
27
|
-
appConfig.theme.blackAsPrimary = blackAsPrimary === 'true'
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function updateFont() {
|
|
32
|
-
const font = localStorage.getItem(`${site.name}-ui-font`)
|
|
33
|
-
if (font) {
|
|
34
|
-
appConfig.theme.font = font
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
updateColor('primary')
|
|
39
|
-
updateColor('neutral')
|
|
40
|
-
updateRadius()
|
|
41
|
-
updateBlackAsPrimary()
|
|
42
|
-
updateFont()
|
|
16
|
+
const icons = localStorage.getItem(`${site.name}-ui-icons`)
|
|
17
|
+
if (icons) appConfig.ui.icons = themeIcons[icons as keyof typeof themeIcons] as any
|
|
43
18
|
}
|
|
44
19
|
|
|
45
|
-
onNuxtReady(() => {
|
|
46
|
-
function updateIcons() {
|
|
47
|
-
const icons = localStorage.getItem(`${site.name}-ui-icons`)
|
|
48
|
-
if (icons) {
|
|
49
|
-
appConfig.theme.icons = icons
|
|
50
|
-
appConfig.ui.icons = themeIcons[icons as keyof typeof themeIcons] as any
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
updateIcons()
|
|
55
|
-
})
|
|
56
|
-
|
|
57
20
|
if (import.meta.server) {
|
|
58
21
|
useHead({
|
|
59
22
|
script: [{
|
|
60
23
|
innerHTML: `
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
24
|
+
var colorsEl = document.querySelector('style#nuxt-ui-colors');
|
|
25
|
+
if (colorsEl) {
|
|
26
|
+
let html = colorsEl.innerHTML;
|
|
27
|
+
if (localStorage.getItem('${site.name}-ui-primary')) {
|
|
28
|
+
const primaryColor = localStorage.getItem('${site.name}-ui-primary');
|
|
29
|
+
if (primaryColor !== 'black') {
|
|
30
|
+
html = html.replace(
|
|
31
|
+
/(--ui-color-primary-\\d{2,3}:\\s*var\\(--color-)${appConfig.ui.colors.primary}(-\\d{2,3}.*?\\))/g,
|
|
32
|
+
\`$1\${primaryColor}$2\`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (localStorage.getItem('${site.name}-ui-neutral')) {
|
|
37
|
+
let neutralColor = localStorage.getItem('${site.name}-ui-neutral');
|
|
66
38
|
html = html.replace(
|
|
67
|
-
/(--ui-color-
|
|
68
|
-
\`$1\${
|
|
39
|
+
/(--ui-color-neutral-\\d{2,3}:\\s*var\\(--color-)${appConfig.ui.colors.neutral}(-\\d{2,3}.*?\\))/g,
|
|
40
|
+
\`$1\${neutralColor === 'neutral' ? 'old-neutral' : neutralColor}$2\`
|
|
69
41
|
);
|
|
70
42
|
}
|
|
71
|
-
}
|
|
72
|
-
if (localStorage.getItem('${site.name}-ui-neutral')) {
|
|
73
|
-
let neutralColor = localStorage.getItem('${site.name}-ui-neutral');
|
|
74
|
-
html = html.replace(
|
|
75
|
-
/(--ui-color-neutral-\\d{2,3}:\\s*var\\(--color-)${appConfig.ui.colors.neutral}(-\\d{2,3}.*?\\))/g,
|
|
76
|
-
\`$1\${neutralColor === 'neutral' ? 'old-neutral' : neutralColor}$2\`
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
43
|
|
|
80
|
-
|
|
44
|
+
colorsEl.innerHTML = html;
|
|
45
|
+
}
|
|
81
46
|
`.replace(/\s+/g, ' '),
|
|
82
47
|
type: 'text/javascript',
|
|
83
48
|
tagPriority: -1
|
|
84
49
|
}, {
|
|
85
50
|
innerHTML: `
|
|
86
51
|
if (localStorage.getItem('${site.name}-ui-radius')) {
|
|
87
|
-
document.
|
|
52
|
+
document.getElementById('${site.name}-ui-radius').innerHTML = ':root { --ui-radius: ' + localStorage.getItem('${site.name}-ui-radius') + 'rem; }';
|
|
88
53
|
}
|
|
89
54
|
`.replace(/\s+/g, ' '),
|
|
90
55
|
type: 'text/javascript',
|
|
@@ -92,18 +57,24 @@ export default defineNuxtPlugin({
|
|
|
92
57
|
}, {
|
|
93
58
|
innerHTML: `
|
|
94
59
|
if (localStorage.getItem('${site.name}-ui-black-as-primary') === 'true') {
|
|
95
|
-
document.
|
|
60
|
+
document.getElementById('${site.name}-ui-black-as-primary').innerHTML = ':root { --ui-primary: black; } .dark { --ui-primary: white; }';
|
|
96
61
|
} else {
|
|
97
|
-
document.
|
|
62
|
+
document.getElementById('${site.name}-ui-black-as-primary').innerHTML = '';
|
|
98
63
|
}
|
|
99
64
|
`.replace(/\s+/g, ' ')
|
|
100
65
|
}, {
|
|
101
|
-
innerHTML:
|
|
102
|
-
if (localStorage.getItem('${site.name}-ui-font')) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
66
|
+
innerHTML: [
|
|
67
|
+
`if (localStorage.getItem('${site.name}-ui-font')) {`,
|
|
68
|
+
`var font = localStorage.getItem('${site.name}-ui-font');`,
|
|
69
|
+
`document.getElementById('${site.name}-ui-font').innerHTML = ':root { --font-sans: \\'' + font + '\\', sans-serif; }';`,
|
|
70
|
+
`if (font !== 'Public Sans') {`,
|
|
71
|
+
`var lnk = document.createElement('link');`,
|
|
72
|
+
`lnk.rel = 'stylesheet';`,
|
|
73
|
+
`lnk.href = 'https://fonts.googleapis.com/css2?family=' + encodeURIComponent(font) + ':wght@400;500;600;700&display=swap';`,
|
|
74
|
+
`lnk.id = 'font-' + font.toLowerCase().replace(/\\s+/g, '-');`,
|
|
75
|
+
`document.head.appendChild(lnk);`,
|
|
76
|
+
`}}`
|
|
77
|
+
].join(' ')
|
|
107
78
|
}]
|
|
108
79
|
})
|
|
109
80
|
}
|
|
@@ -4,6 +4,8 @@ if (!page.value) {
|
|
|
4
4
|
throw createError({ status: 404, statusText: 'Page not found', fatal: true })
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
const site = useSiteConfig()
|
|
8
|
+
|
|
7
9
|
const title = page.value.seo?.title || page.value.title
|
|
8
10
|
const description = page.value.seo?.description || page.value.description
|
|
9
11
|
|
|
@@ -15,9 +17,10 @@ useSeoMeta({
|
|
|
15
17
|
ogDescription: description
|
|
16
18
|
})
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
defineOgImage('Docs', {
|
|
19
21
|
title,
|
|
20
|
-
description
|
|
22
|
+
description,
|
|
23
|
+
siteName: site.name
|
|
21
24
|
})
|
|
22
25
|
</script>
|
|
23
26
|
|
|
@@ -6,6 +6,8 @@ if (!page.value) {
|
|
|
6
6
|
throw createError({ status: 404, statusText: 'Page not found', fatal: true })
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
const site = useSiteConfig()
|
|
10
|
+
|
|
9
11
|
const title = page.value.seo?.title || page.value.title
|
|
10
12
|
const description = page.value.seo?.description || page.value.description
|
|
11
13
|
|
|
@@ -16,9 +18,10 @@ useSeoMeta({
|
|
|
16
18
|
ogDescription: description
|
|
17
19
|
})
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
defineOgImage('Docs', {
|
|
20
22
|
title,
|
|
21
|
-
description
|
|
23
|
+
description,
|
|
24
|
+
siteName: site.name
|
|
22
25
|
})
|
|
23
26
|
|
|
24
27
|
const { data: versions } = page.value.releases
|
package/app/types/index.d.ts
CHANGED
|
@@ -6,10 +6,6 @@ export interface ExtendedButtonProps extends ButtonProps {
|
|
|
6
6
|
|
|
7
7
|
declare module 'nuxt/schema' {
|
|
8
8
|
interface AppConfig {
|
|
9
|
-
vercelAnalytics: {
|
|
10
|
-
enable: boolean
|
|
11
|
-
debug: boolean
|
|
12
|
-
}
|
|
13
9
|
seo: {
|
|
14
10
|
titleTemplate: string
|
|
15
11
|
title: string
|
|
@@ -71,7 +67,7 @@ declare module 'nuxt/schema' {
|
|
|
71
67
|
* 在文档侧边栏中显示“使用 AI 进行解释”按钮。
|
|
72
68
|
* @default true
|
|
73
69
|
*/
|
|
74
|
-
explainWithAi
|
|
70
|
+
explainWithAi: boolean
|
|
75
71
|
/**
|
|
76
72
|
* 显示的常见问题解答问题。
|
|
77
73
|
* @example 简单格式: ['如何安装?', '如何配置?']
|
|
@@ -97,16 +93,6 @@ declare module 'nuxt/schema' {
|
|
|
97
93
|
* @default 'AI 助手'
|
|
98
94
|
*/
|
|
99
95
|
title: string
|
|
100
|
-
/**
|
|
101
|
-
* 折叠按钮的文本。
|
|
102
|
-
* @default '折叠‘
|
|
103
|
-
*/
|
|
104
|
-
collapse: string
|
|
105
|
-
/**
|
|
106
|
-
* 展开按钮的文本。
|
|
107
|
-
* @default '展开'
|
|
108
|
-
*/
|
|
109
|
-
expand: string
|
|
110
96
|
/**
|
|
111
97
|
* 清除聊天记录按钮的文本。
|
|
112
98
|
* @default '清除聊天记录'
|
|
@@ -117,26 +103,6 @@ declare module 'nuxt/schema' {
|
|
|
117
103
|
* @default '关闭'
|
|
118
104
|
*/
|
|
119
105
|
close: string
|
|
120
|
-
/**
|
|
121
|
-
* 加载时的提示文本。
|
|
122
|
-
* @default 'Loading...'
|
|
123
|
-
*/
|
|
124
|
-
loading: string
|
|
125
|
-
/**
|
|
126
|
-
* 询问任何事情文本
|
|
127
|
-
* @default '问我任何事情...'
|
|
128
|
-
*/
|
|
129
|
-
askAnything: string
|
|
130
|
-
/**
|
|
131
|
-
* 询问任何事情描述文本
|
|
132
|
-
* @default '我可以帮助您浏览文档、解释概念并回答您的问题。'
|
|
133
|
-
*/
|
|
134
|
-
askMeAnythingDescription: string
|
|
135
|
-
/**
|
|
136
|
-
* FAQ 建议标题文本。
|
|
137
|
-
* @default 'FAQ 建议'
|
|
138
|
-
*/
|
|
139
|
-
faq: string
|
|
140
106
|
/**
|
|
141
107
|
* 浮动输入框的占位符文本。
|
|
142
108
|
* @default '输入你的问题...'
|
|
@@ -152,19 +118,9 @@ declare module 'nuxt/schema' {
|
|
|
152
118
|
* @default '与 AI 聊天'
|
|
153
119
|
*/
|
|
154
120
|
trigger: string
|
|
155
|
-
/**
|
|
156
|
-
* 思考时的提示文本。
|
|
157
|
-
* @default '思考中...'
|
|
158
|
-
*/
|
|
159
|
-
streaming: string
|
|
160
|
-
/**
|
|
161
|
-
* 思考后的提示文本。
|
|
162
|
-
* @default '思考过程'
|
|
163
|
-
*/
|
|
164
|
-
streamed: string
|
|
165
121
|
/**
|
|
166
122
|
* 使用 AI 进行解释按钮的文本。
|
|
167
|
-
* @default '用 AI 解释此页面
|
|
123
|
+
* @default '用 AI 解释此页面'
|
|
168
124
|
*/
|
|
169
125
|
explainWithAi: string
|
|
170
126
|
}
|
|
@@ -172,11 +128,6 @@ declare module 'nuxt/schema' {
|
|
|
172
128
|
* 图标配置。
|
|
173
129
|
*/
|
|
174
130
|
icons: {
|
|
175
|
-
/**
|
|
176
|
-
* 加载时的图标。
|
|
177
|
-
* @default i-lucide-loader
|
|
178
|
-
*/
|
|
179
|
-
loading: string
|
|
180
131
|
/**
|
|
181
132
|
* AI 聊天触发按钮和滑出层头部的图标。
|
|
182
133
|
* @default 'i-custom-ai'
|
|
@@ -184,28 +135,28 @@ declare module 'nuxt/schema' {
|
|
|
184
135
|
trigger: string
|
|
185
136
|
/**
|
|
186
137
|
* "使用 AI 进行解释" 按钮的图标。
|
|
187
|
-
* @default 'i-lucide-
|
|
138
|
+
* @default 'i-lucide-bot-message-square'
|
|
188
139
|
*/
|
|
189
140
|
explain: string
|
|
190
141
|
/**
|
|
191
142
|
* 思考时的图标。
|
|
192
|
-
* @default
|
|
143
|
+
* @default 'i-lucide-brain'
|
|
193
144
|
*/
|
|
194
|
-
|
|
145
|
+
reasoning: string
|
|
195
146
|
/**
|
|
196
147
|
* 清除聊天记录按钮的图标。
|
|
197
|
-
* @default 'i-lucide-
|
|
148
|
+
* @default 'i-lucide-list-x'
|
|
198
149
|
*/
|
|
199
150
|
clearChat: string
|
|
200
151
|
/**
|
|
201
152
|
* 关闭按钮的图标。
|
|
202
|
-
* @default 'i-lucide-
|
|
153
|
+
* @default 'i-lucide-panel-right-close'
|
|
203
154
|
*/
|
|
204
155
|
close: string
|
|
205
156
|
/**
|
|
206
157
|
* 用于映射不同 AI 提供商的图标。
|
|
207
158
|
* @example { mistral: 'i-simple-icons-mistralai' }
|
|
208
|
-
* @default {
|
|
159
|
+
* @default { deepseek: 'i-hugeicons:deepseek', alibaba: 'i-hugeicons:qwen', zai: 'i-simple-icons:zig', moonshotai: 'i-hugeicons:kimi-ai', xai: 'i-hugeicons:grok-02' }
|
|
209
160
|
*/
|
|
210
161
|
providers: Record<string, string>
|
|
211
162
|
}
|
package/content.config.ts
CHANGED