@movk/nuxt-docs 1.12.1 → 1.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/app.config.ts +2 -1
- package/app/app.vue +1 -1
- package/app/assets/css/main.css +0 -16
- package/modules/ai-chat/index.ts +3 -3
- package/modules/ai-chat/runtime/components/AiChatFloatingInput.vue +39 -30
- package/modules/ai-chat/runtime/components/AiChatPanel.vue +22 -33
- package/modules/ai-chat/runtime/composables/useHighlighter.ts +21 -9
- package/modules/config.ts +2 -1
- package/nuxt.config.ts +29 -37
- package/package.json +13 -9
- package/server/plugins/llms.ts +11 -2
- package/utils/meta.ts +9 -4
- package/app/mdc.config.ts +0 -10
- package/app/utils/shiki-transformer-icon-highlight.ts +0 -89
package/app/app.config.ts
CHANGED
|
@@ -108,7 +108,8 @@ export default defineAppConfig({
|
|
|
108
108
|
streaming: 'i-lucide-chevron-down',
|
|
109
109
|
providers: {
|
|
110
110
|
deepseek: 'i-ri-deepseek-fill',
|
|
111
|
-
alibaba: 'i-simple-icons-alibabacloud'
|
|
111
|
+
alibaba: 'i-simple-icons-alibabacloud',
|
|
112
|
+
zai: 'i-simple-icons:zig'
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
}
|
package/app/app.vue
CHANGED
|
@@ -64,8 +64,8 @@ provide('navigation', rootNavigation)
|
|
|
64
64
|
<ClientOnly>
|
|
65
65
|
<LazyUContentSearch :files="files" :navigation="rootNavigation" :fuse="{ resultLimit: 1000 }" />
|
|
66
66
|
<template v-if="isAiChatEnabled">
|
|
67
|
-
<LazyAiChatFloatingInput />
|
|
68
67
|
<LazyAiChatPanel />
|
|
68
|
+
<LazyAiChatFloatingInput />
|
|
69
69
|
</template>
|
|
70
70
|
</ClientOnly>
|
|
71
71
|
</template>
|
package/app/assets/css/main.css
CHANGED
|
@@ -22,19 +22,3 @@
|
|
|
22
22
|
:root {
|
|
23
23
|
--ui-container: var(--container-8xl);
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
/* Shiki icon highlight transformer styles */
|
|
27
|
-
.shiki-icon-highlight {
|
|
28
|
-
display: inline-block;
|
|
29
|
-
width: 1.25em;
|
|
30
|
-
height: 1.25em;
|
|
31
|
-
vertical-align: -0.25em;
|
|
32
|
-
margin-right: 0.125em;
|
|
33
|
-
background-color: var(--ui-text-highlighted);
|
|
34
|
-
-webkit-mask-repeat: no-repeat;
|
|
35
|
-
mask-repeat: no-repeat;
|
|
36
|
-
-webkit-mask-size: 100% 100%;
|
|
37
|
-
mask-size: 100% 100%;
|
|
38
|
-
-webkit-mask-image: var(--shiki-icon-url);
|
|
39
|
-
mask-image: var(--shiki-icon-url);
|
|
40
|
-
}
|
package/modules/ai-chat/index.ts
CHANGED
|
@@ -32,7 +32,7 @@ export interface AiChatModuleOptions {
|
|
|
32
32
|
models?: string[]
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
const log = logger.withTag('movk-nuxt-docs
|
|
35
|
+
const log = logger.withTag('movk-nuxt-docs')
|
|
36
36
|
|
|
37
37
|
export default defineNuxtModule<AiChatModuleOptions>({
|
|
38
38
|
meta: {
|
|
@@ -77,7 +77,7 @@ export default defineNuxtModule<AiChatModuleOptions>({
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
if (!hasApiKey) {
|
|
80
|
-
log.warn('[
|
|
80
|
+
log.warn('[movk-nuxt-docs] Ai Chat Module disabled: no API key found in environment variables.')
|
|
81
81
|
return
|
|
82
82
|
}
|
|
83
83
|
|
|
@@ -119,7 +119,7 @@ export default defineNuxtModule<AiChatModuleOptions>({
|
|
|
119
119
|
handler: resolve('./runtime/server/api/ai-chat')
|
|
120
120
|
})
|
|
121
121
|
} else {
|
|
122
|
-
log.info(`[
|
|
122
|
+
log.info(`[movk-nuxt-docs] Using custom handler, skipping default handler registration`)
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
})
|
|
@@ -61,39 +61,48 @@ defineShortcuts(shortcuts)
|
|
|
61
61
|
:animate="{ y: 0, opacity: 1 }"
|
|
62
62
|
:exit="{ y: 100, opacity: 0 }"
|
|
63
63
|
:transition="{ duration: 0.2, ease: 'easeOut' }"
|
|
64
|
-
class="fixed
|
|
64
|
+
class="fixed inset-x-0 z-10 px-4 sm:px-80 bottom-[max(1.5rem,env(safe-area-inset-bottom))]"
|
|
65
65
|
style="will-change: transform"
|
|
66
66
|
>
|
|
67
|
-
<form
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
67
|
+
<form
|
|
68
|
+
class="flex w-full justify-center"
|
|
69
|
+
@submit.prevent="handleSubmit"
|
|
70
|
+
>
|
|
71
|
+
<div class="w-full max-w-96">
|
|
72
|
+
<UInput
|
|
73
|
+
ref="inputRef"
|
|
74
|
+
v-model="input"
|
|
75
|
+
:placeholder="aiChat.texts.placeholder"
|
|
76
|
+
size="lg"
|
|
77
|
+
maxlength="1000"
|
|
78
|
+
:ui="{
|
|
79
|
+
root: 'group w-full! min-w-0 sm:max-w-96 transition-all duration-300 ease-out [@media(hover:hover)]:hover:scale-105 [@media(hover:hover)]:focus-within:scale-105',
|
|
80
|
+
base: 'bg-default shadow-lg text-base',
|
|
81
|
+
trailing: 'pe-2'
|
|
82
|
+
}"
|
|
83
|
+
@keydown.enter.exact.prevent="handleSubmit"
|
|
84
|
+
>
|
|
85
|
+
<template #trailing>
|
|
86
|
+
<div class="flex items-center gap-2">
|
|
87
|
+
<div class="hidden sm:flex group-focus-within:hidden items-center gap-1">
|
|
88
|
+
<UKbd
|
|
89
|
+
v-for="key in shortcutDisplayKeys"
|
|
90
|
+
:key="key"
|
|
91
|
+
:value="key"
|
|
92
|
+
/>
|
|
93
|
+
</div>
|
|
85
94
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
</
|
|
95
|
-
</
|
|
96
|
-
</
|
|
95
|
+
<UButton
|
|
96
|
+
type="submit"
|
|
97
|
+
icon="i-lucide-arrow-up"
|
|
98
|
+
color="primary"
|
|
99
|
+
size="xs"
|
|
100
|
+
:disabled="!input.trim()"
|
|
101
|
+
/>
|
|
102
|
+
</div>
|
|
103
|
+
</template>
|
|
104
|
+
</UInput>
|
|
105
|
+
</div>
|
|
97
106
|
</form>
|
|
98
107
|
</motion.div>
|
|
99
108
|
</AnimatePresence>
|
|
@@ -121,13 +121,10 @@ onMounted(() => {
|
|
|
121
121
|
<template>
|
|
122
122
|
<DefineChatContent v-slot="{ showExpandButton = true }">
|
|
123
123
|
<div class="flex h-full flex-col">
|
|
124
|
-
<div class="flex h-16 shrink-0 items-center justify-between border-b border-
|
|
124
|
+
<div class="flex h-16 shrink-0 items-center justify-between border-b border-default px-4">
|
|
125
125
|
<span class="font-medium text-highlighted">{{ aiChat.texts.title }}</span>
|
|
126
126
|
<div class="flex items-center gap-1">
|
|
127
|
-
<UTooltip
|
|
128
|
-
v-if="showExpandButton"
|
|
129
|
-
:text="isExpanded ? aiChat.texts.collapse : aiChat.texts.expand"
|
|
130
|
-
>
|
|
127
|
+
<UTooltip v-if="showExpandButton" :text="isExpanded ? aiChat.texts.collapse : aiChat.texts.expand">
|
|
131
128
|
<UButton
|
|
132
129
|
:icon="isExpanded ? 'i-lucide-minimize-2' : 'i-lucide-maximize-2'"
|
|
133
130
|
color="neutral"
|
|
@@ -138,10 +135,7 @@ onMounted(() => {
|
|
|
138
135
|
@click="toggleExpanded"
|
|
139
136
|
/>
|
|
140
137
|
</UTooltip>
|
|
141
|
-
<UTooltip
|
|
142
|
-
v-if="chat.messages.length > 0"
|
|
143
|
-
:text="aiChat.texts.clearChat"
|
|
144
|
-
>
|
|
138
|
+
<UTooltip v-if="chat.messages.length > 0" :text="aiChat.texts.clearChat">
|
|
145
139
|
<UButton
|
|
146
140
|
:icon="aiChat.icons.clearChat"
|
|
147
141
|
color="neutral"
|
|
@@ -183,7 +177,11 @@ onMounted(() => {
|
|
|
183
177
|
v-for="(part, index) in message.parts"
|
|
184
178
|
:key="`${message.id}-${part.type}-${index}${'state' in part ? `-${part.state}` : ''}`"
|
|
185
179
|
>
|
|
186
|
-
<AiChatReasoning
|
|
180
|
+
<AiChatReasoning
|
|
181
|
+
v-if="part.type === 'reasoning'"
|
|
182
|
+
:text="part.text"
|
|
183
|
+
:is-streaming="part.state !== 'done'"
|
|
184
|
+
/>
|
|
187
185
|
|
|
188
186
|
<MDCCached
|
|
189
187
|
v-else-if="part.type === 'text'"
|
|
@@ -217,19 +215,10 @@ onMounted(() => {
|
|
|
217
215
|
</template>
|
|
218
216
|
</UChatMessages>
|
|
219
217
|
|
|
220
|
-
<div
|
|
221
|
-
v-
|
|
222
|
-
class="p-4"
|
|
223
|
-
>
|
|
224
|
-
<div
|
|
225
|
-
v-if="!faqQuestions?.length"
|
|
226
|
-
class="flex h-full flex-col items-center justify-center py-12 text-center"
|
|
227
|
-
>
|
|
218
|
+
<div v-else class="p-4">
|
|
219
|
+
<div v-if="!faqQuestions?.length" class="flex h-full flex-col items-center justify-center py-12 text-center">
|
|
228
220
|
<div class="mb-4 flex size-12 items-center justify-center rounded-full bg-primary/10">
|
|
229
|
-
<UIcon
|
|
230
|
-
name="i-lucide-message-circle-question"
|
|
231
|
-
class="size-6 text-primary"
|
|
232
|
-
/>
|
|
221
|
+
<UIcon name="i-lucide-message-circle-question" class="size-6 text-primary" />
|
|
233
222
|
</div>
|
|
234
223
|
<h3 class="mb-2 text-base font-medium text-highlighted">
|
|
235
224
|
{{ aiChat.texts.askAnything }}
|
|
@@ -253,17 +242,16 @@ onMounted(() => {
|
|
|
253
242
|
<UChatPrompt
|
|
254
243
|
v-model="input"
|
|
255
244
|
:rows="2"
|
|
256
|
-
class="text-sm"
|
|
257
|
-
variant="subtle"
|
|
258
245
|
:placeholder="aiChat.texts.placeholder"
|
|
246
|
+
maxlength="1000"
|
|
259
247
|
:ui="{
|
|
260
248
|
root: 'shadow-none!',
|
|
261
|
-
body: '*:p-0! *:rounded-none!'
|
|
249
|
+
body: '*:p-0! *:rounded-none! *:text-sm!'
|
|
262
250
|
}"
|
|
263
251
|
@submit="handleSubmit"
|
|
264
252
|
>
|
|
265
253
|
<template #footer>
|
|
266
|
-
<div class="
|
|
254
|
+
<div class="flex items-center gap-1 text-xs text-muted">
|
|
267
255
|
<AiChatModelSelect v-model="model" />
|
|
268
256
|
<div class="flex gap-1 justify-between items-center px-1 text-xs text-muted">
|
|
269
257
|
<span>{{ aiChat.texts.lineBreak }}</span>
|
|
@@ -281,22 +269,25 @@ onMounted(() => {
|
|
|
281
269
|
/>
|
|
282
270
|
</template>
|
|
283
271
|
</UChatPrompt>
|
|
272
|
+
<div class="mt-1 flex text-xs text-dimmed items-center justify-between">
|
|
273
|
+
<span>刷新时聊天会被清除</span>
|
|
274
|
+
<span>
|
|
275
|
+
{{ input.length }}/1000
|
|
276
|
+
</span>
|
|
277
|
+
</div>
|
|
284
278
|
</div>
|
|
285
279
|
</div>
|
|
286
280
|
</DefineChatContent>
|
|
287
281
|
|
|
288
282
|
<aside
|
|
289
283
|
v-if="!isMobile"
|
|
290
|
-
class="fixed top-0 z-50 h-dvh overflow-hidden border-l border-
|
|
284
|
+
class="left-auto! fixed top-0 z-50 h-dvh overflow-hidden border-l border-default bg-default/95 backdrop-blur-xl transition-[right,width] duration-200 ease-linear will-change-[right,width]"
|
|
291
285
|
:style="{
|
|
292
286
|
width: `${panelWidth}px`,
|
|
293
287
|
right: isOpen ? '0' : `-${panelWidth}px`
|
|
294
288
|
}"
|
|
295
289
|
>
|
|
296
|
-
<div
|
|
297
|
-
class="h-full transition-[width] duration-200 ease-linear"
|
|
298
|
-
:style="{ width: `${panelWidth}px` }"
|
|
299
|
-
>
|
|
290
|
+
<div class="h-full transition-[width] duration-200 ease-linear" :style="{ width: `${panelWidth}px` }">
|
|
300
291
|
<ReuseChatContent :show-expand-button="true" />
|
|
301
292
|
</div>
|
|
302
293
|
</aside>
|
|
@@ -304,8 +295,6 @@ onMounted(() => {
|
|
|
304
295
|
<USlideover
|
|
305
296
|
v-else
|
|
306
297
|
v-model:open="isOpen"
|
|
307
|
-
title=" "
|
|
308
|
-
description=" "
|
|
309
298
|
side="right"
|
|
310
299
|
:ui="{
|
|
311
300
|
content: 'ring-0 bg-default'
|
|
@@ -1,16 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
3
|
-
import { createJavaScriptRegexEngine } from '
|
|
1
|
+
import { createHighlighterCore } from '@shikijs/core'
|
|
2
|
+
import type { HighlighterCore } from '@shikijs/core'
|
|
3
|
+
import { createJavaScriptRegexEngine } from '@shikijs/engine-javascript'
|
|
4
4
|
|
|
5
|
-
let highlighter:
|
|
6
|
-
|
|
7
|
-
let promise: Promise<HighlighterGeneric<any, any>> | null = null
|
|
5
|
+
let highlighter: HighlighterCore | null = null
|
|
6
|
+
let promise: Promise<HighlighterCore> | null = null
|
|
8
7
|
|
|
9
8
|
export const useHighlighter = async () => {
|
|
10
9
|
if (!promise) {
|
|
11
|
-
promise =
|
|
12
|
-
langs: [
|
|
13
|
-
|
|
10
|
+
promise = createHighlighterCore({
|
|
11
|
+
langs: [
|
|
12
|
+
import('@shikijs/langs/vue'),
|
|
13
|
+
import('@shikijs/langs/javascript'),
|
|
14
|
+
import('@shikijs/langs/typescript'),
|
|
15
|
+
import('@shikijs/langs/css'),
|
|
16
|
+
import('@shikijs/langs/html'),
|
|
17
|
+
import('@shikijs/langs/json'),
|
|
18
|
+
import('@shikijs/langs/yaml'),
|
|
19
|
+
import('@shikijs/langs/markdown'),
|
|
20
|
+
import('@shikijs/langs/bash')
|
|
21
|
+
],
|
|
22
|
+
themes: [
|
|
23
|
+
import('@shikijs/themes/material-theme-palenight'),
|
|
24
|
+
import('@shikijs/themes/material-theme-lighter')
|
|
25
|
+
],
|
|
14
26
|
engine: createJavaScriptRegexEngine()
|
|
15
27
|
})
|
|
16
28
|
}
|
package/modules/config.ts
CHANGED
|
@@ -18,7 +18,8 @@ export default defineNuxtModule({
|
|
|
18
18
|
const meta = await getPackageJsonMetadata(dir)
|
|
19
19
|
const gitInfo = await getLocalGitInfo(dir) || getGitEnv()
|
|
20
20
|
|
|
21
|
-
const
|
|
21
|
+
const site = nuxt.options.site
|
|
22
|
+
const siteName = (site && site.name) || meta.name || gitInfo?.name || ''
|
|
22
23
|
|
|
23
24
|
nuxt.options.site = defu(nuxt.options.site, {
|
|
24
25
|
url,
|
package/nuxt.config.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { defineNuxtConfig } from 'nuxt/config'
|
|
2
2
|
import pkg from './package.json'
|
|
3
|
+
import { createResolver } from '@nuxt/kit'
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
const WASM_EXTERNALS = ['env', 'wasi_snapshot_preview1']
|
|
6
|
-
|
|
7
|
-
function mergeExternals(existing: unknown, additions: string[]): string[] {
|
|
8
|
-
if (Array.isArray(existing)) return [...existing, ...additions]
|
|
9
|
-
if (typeof existing === 'string') return [existing, ...additions]
|
|
10
|
-
return additions
|
|
11
|
-
}
|
|
5
|
+
const { resolve } = createResolver(import.meta.url)
|
|
12
6
|
|
|
13
7
|
export default defineNuxtConfig({
|
|
14
8
|
modules: [
|
|
9
|
+
resolve('./modules/config'),
|
|
10
|
+
resolve('./modules/routing'),
|
|
11
|
+
resolve('./modules/md-rewrite'),
|
|
12
|
+
resolve('./modules/component-example'),
|
|
13
|
+
resolve('./modules/css'),
|
|
15
14
|
'@nuxt/ui',
|
|
16
15
|
'@nuxt/content',
|
|
17
16
|
'@nuxt/image',
|
|
18
17
|
'@nuxt/a11y',
|
|
18
|
+
'@nuxtjs/robots',
|
|
19
19
|
'@nuxtjs/mcp-toolkit',
|
|
20
20
|
'@vueuse/nuxt',
|
|
21
21
|
'nuxt-component-meta',
|
|
@@ -50,7 +50,8 @@ export default defineNuxtConfig({
|
|
|
50
50
|
|
|
51
51
|
mdc: {
|
|
52
52
|
highlight: {
|
|
53
|
-
noApiRoute: false
|
|
53
|
+
noApiRoute: false,
|
|
54
|
+
shikiEngine: 'javascript'
|
|
54
55
|
}
|
|
55
56
|
},
|
|
56
57
|
|
|
@@ -76,9 +77,22 @@ export default defineNuxtConfig({
|
|
|
76
77
|
crawlLinks: true,
|
|
77
78
|
failOnError: false,
|
|
78
79
|
autoSubfolderIndex: false
|
|
80
|
+
},
|
|
81
|
+
compatibilityDate: {
|
|
82
|
+
// Don't generate observability routes for now
|
|
83
|
+
vercel: '2025-07-14'
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
vite: {
|
|
88
|
+
build: {
|
|
89
|
+
sourcemap: false,
|
|
90
|
+
chunkSizeWarningLimit: 1024
|
|
79
91
|
}
|
|
80
92
|
},
|
|
81
93
|
|
|
94
|
+
telemetry: false,
|
|
95
|
+
|
|
82
96
|
hooks: {
|
|
83
97
|
'vite:extendConfig': async (config) => {
|
|
84
98
|
// Ensure optimizeDeps.include exists
|
|
@@ -103,28 +117,6 @@ export default defineNuxtConfig({
|
|
|
103
117
|
'@movk/nuxt-docs > mermaid > d3',
|
|
104
118
|
'@movk/nuxt-docs > mermaid > dompurify'
|
|
105
119
|
)
|
|
106
|
-
|
|
107
|
-
// WASM plugin support for Shiki
|
|
108
|
-
const [wasm, topLevelAwait] = await Promise.all([
|
|
109
|
-
import('vite-plugin-wasm'),
|
|
110
|
-
import('vite-plugin-top-level-await')
|
|
111
|
-
])
|
|
112
|
-
config.plugins!.push(wasm.default(), topLevelAwait.default() as any)
|
|
113
|
-
|
|
114
|
-
const build = config.build || ((config as any).build = {})
|
|
115
|
-
build.rollupOptions ??= {}
|
|
116
|
-
build.rollupOptions.external = mergeExternals(
|
|
117
|
-
build.rollupOptions.external,
|
|
118
|
-
WASM_EXTERNALS
|
|
119
|
-
)
|
|
120
|
-
},
|
|
121
|
-
|
|
122
|
-
'nitro:config': (nitroConfig) => {
|
|
123
|
-
nitroConfig.rollupConfig ??= {}
|
|
124
|
-
nitroConfig.rollupConfig.external = mergeExternals(
|
|
125
|
-
nitroConfig.rollupConfig.external,
|
|
126
|
-
WASM_EXTERNALS
|
|
127
|
-
)
|
|
128
120
|
}
|
|
129
121
|
},
|
|
130
122
|
|
|
@@ -156,12 +148,12 @@ export default defineNuxtConfig({
|
|
|
156
148
|
fonts: {
|
|
157
149
|
families: [
|
|
158
150
|
{ 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'
|
|
151
|
+
{ name: 'DM Sans', provider: 'google' },
|
|
152
|
+
{ name: 'Geist', provider: 'google' },
|
|
153
|
+
{ name: 'Inter', provider: 'google' },
|
|
154
|
+
{ name: 'Poppins', provider: 'google' },
|
|
155
|
+
{ name: 'Outfit', provider: 'google' },
|
|
156
|
+
{ name: 'Raleway', provider: 'google' }
|
|
165
157
|
]
|
|
166
158
|
},
|
|
167
159
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@movk/nuxt-docs",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.12.
|
|
4
|
+
"version": "1.12.3",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Modern Nuxt 4 documentation theme with auto-generated component docs, AI chat assistant, MCP server, and complete developer experience optimization.",
|
|
7
7
|
"author": "YiXuan <mhaibaraai@gmail.com>",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@ai-sdk/gateway": "^3.0.39",
|
|
33
33
|
"@ai-sdk/mcp": "^1.0.19",
|
|
34
|
-
"@ai-sdk/vue": "^3.0.
|
|
34
|
+
"@ai-sdk/vue": "^3.0.78",
|
|
35
35
|
"@iconify-json/lucide": "^1.2.89",
|
|
36
36
|
"@iconify-json/simple-icons": "^1.2.70",
|
|
37
37
|
"@iconify-json/vscode-icons": "^1.2.40",
|
|
@@ -43,13 +43,18 @@
|
|
|
43
43
|
"@nuxt/ui": "^4.4.0",
|
|
44
44
|
"@nuxtjs/mcp-toolkit": "^0.6.3",
|
|
45
45
|
"@nuxtjs/mdc": "^0.20.1",
|
|
46
|
+
"@nuxtjs/robots": "^5.7.0",
|
|
46
47
|
"@octokit/rest": "^22.0.1",
|
|
47
|
-
"@openrouter/ai-sdk-provider": "^2.
|
|
48
|
+
"@openrouter/ai-sdk-provider": "^2.2.1",
|
|
49
|
+
"@shikijs/core": "^3.22.0",
|
|
50
|
+
"@shikijs/engine-javascript": "^3.22.0",
|
|
51
|
+
"@shikijs/langs": "^3.22.0",
|
|
52
|
+
"@shikijs/themes": "^3.22.0",
|
|
48
53
|
"@vercel/analytics": "^1.6.1",
|
|
49
54
|
"@vercel/speed-insights": "^1.3.1",
|
|
50
55
|
"@vueuse/core": "^14.2.0",
|
|
51
56
|
"@vueuse/nuxt": "^14.2.0",
|
|
52
|
-
"ai": "^6.0.
|
|
57
|
+
"ai": "^6.0.78",
|
|
53
58
|
"defu": "^6.1.4",
|
|
54
59
|
"dompurify": "^3.3.1",
|
|
55
60
|
"exsolve": "^1.0.8",
|
|
@@ -58,7 +63,7 @@
|
|
|
58
63
|
"minimark": "^0.2.0",
|
|
59
64
|
"motion-v": "^1.10.2",
|
|
60
65
|
"nuxt": "^4.3.1",
|
|
61
|
-
"nuxt-component-meta": "^0.17.
|
|
66
|
+
"nuxt-component-meta": "^0.17.2",
|
|
62
67
|
"nuxt-llms": "^0.2.0",
|
|
63
68
|
"nuxt-og-image": "^5.1.13",
|
|
64
69
|
"ohash": "^2.0.11",
|
|
@@ -66,14 +71,13 @@
|
|
|
66
71
|
"pkg-types": "^2.3.0",
|
|
67
72
|
"prettier": "^3.8.1",
|
|
68
73
|
"scule": "^1.3.0",
|
|
69
|
-
"shiki": "^3.22.0",
|
|
70
74
|
"shiki-stream": "^0.1.4",
|
|
71
75
|
"tailwind-merge": "^3.4.0",
|
|
72
76
|
"tailwindcss": "^4.1.18",
|
|
73
77
|
"ufo": "^1.6.3",
|
|
78
|
+
"unist-util-visit": "^5.1.0",
|
|
74
79
|
"vue-component-meta": "^3.2.4",
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"zod": "^4.3.6"
|
|
80
|
+
"zod": "^4.3.6",
|
|
81
|
+
"zod-to-json-schema": "^3.25.1"
|
|
78
82
|
}
|
|
79
83
|
}
|
package/server/plugins/llms.ts
CHANGED
|
@@ -6,8 +6,17 @@ export default defineNitroPlugin((nitroApp) => {
|
|
|
6
6
|
await transformMDC(event, doc as any)
|
|
7
7
|
})
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
nitroApp.hooks.hook('llms:generate', (_, { sections, domain }) => {
|
|
10
|
+
// Transform links except for "Documentation Sets"
|
|
11
|
+
sections.forEach((section) => {
|
|
12
|
+
if (section.title !== 'Documentation Sets') {
|
|
13
|
+
section.links = section.links?.map(link => ({
|
|
14
|
+
...link,
|
|
15
|
+
href: `${link.href.replace(new RegExp(`^${domain}`), `${domain}/raw`)}.md`
|
|
16
|
+
}))
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
|
|
11
20
|
// Move "Documentation Sets" to the end
|
|
12
21
|
const docSetIdx = sections.findIndex((s: any) => s.title === 'Documentation Sets')
|
|
13
22
|
if (docSetIdx !== -1) {
|
package/utils/meta.ts
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
// copy from https://github.com/nuxt-content/docus/tree/main/layer/utils
|
|
2
1
|
import { readFile } from 'node:fs/promises'
|
|
3
2
|
import { resolve } from 'node:path'
|
|
3
|
+
import { withHttps } from 'ufo'
|
|
4
4
|
|
|
5
5
|
export function inferSiteURL() {
|
|
6
6
|
// https://github.com/unjs/std-env/issues/59
|
|
7
|
-
|
|
8
|
-
process.env.
|
|
9
|
-
||
|
|
7
|
+
const url = (
|
|
8
|
+
process.env.NUXT_PUBLIC_SITE_URL // Nuxt public runtime config
|
|
9
|
+
|| process.env.NUXT_SITE_URL // Nuxt site config
|
|
10
|
+
|| process.env.VERCEL_PROJECT_PRODUCTION_URL // Vercel production URL
|
|
11
|
+
|| process.env.VERCEL_BRANCH_URL // Vercel branch URL
|
|
12
|
+
|| process.env.VERCEL_URL // Vercel deployment URL
|
|
10
13
|
|| process.env.URL // Netlify
|
|
11
14
|
|| process.env.CI_PAGES_URL // Gitlab Pages
|
|
12
15
|
|| process.env.CF_PAGES_URL // Cloudflare Pages
|
|
13
16
|
)
|
|
17
|
+
|
|
18
|
+
return url ? withHttps(url) : undefined
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
export async function getPackageJsonMetadata(dir: string) {
|
package/app/mdc.config.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import type { ShikiTransformer } from 'shiki'
|
|
2
|
-
|
|
3
|
-
export interface TransformerIconHighlightOptions {
|
|
4
|
-
/**
|
|
5
|
-
* Custom function to render the icon HTML
|
|
6
|
-
* @default Uses Iconify API with mask mode
|
|
7
|
-
*/
|
|
8
|
-
htmlIcon?: (icon: string) => string
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// Common icon collections to validate against (sorted by length descending for proper matching)
|
|
12
|
-
const ICON_COLLECTIONS = [
|
|
13
|
-
'simple-icons',
|
|
14
|
-
'vscode-icons',
|
|
15
|
-
'tabler',
|
|
16
|
-
'lucide',
|
|
17
|
-
'logos',
|
|
18
|
-
'ph'
|
|
19
|
-
]
|
|
20
|
-
|
|
21
|
-
function parseIconName(text: string): { collection: string, name: string, format: 'i' | 'colon' } | null {
|
|
22
|
-
// Strip quotes if present (single, double, or backticks)
|
|
23
|
-
let cleanText = text
|
|
24
|
-
if (/^['"`].*['"`]$/.test(text)) {
|
|
25
|
-
cleanText = text.slice(1, -1)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Try i-{collection}-{name} format
|
|
29
|
-
if (cleanText.startsWith('i-')) {
|
|
30
|
-
const rest = cleanText.slice(2)
|
|
31
|
-
for (const collection of ICON_COLLECTIONS) {
|
|
32
|
-
if (rest.startsWith(`${collection}-`)) {
|
|
33
|
-
const name = rest.slice(collection.length + 1)
|
|
34
|
-
if (name && /^[a-z0-9]+(?:-[a-z0-9]+)*$/i.test(name)) {
|
|
35
|
-
return { collection, name, format: 'i' }
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Try {collection}:{name} format
|
|
42
|
-
const colonIndex = cleanText.indexOf(':')
|
|
43
|
-
if (colonIndex > 0) {
|
|
44
|
-
const collection = cleanText.slice(0, colonIndex)
|
|
45
|
-
const name = cleanText.slice(colonIndex + 1)
|
|
46
|
-
if (ICON_COLLECTIONS.includes(collection) && name && /^[a-z0-9]+(?:-[a-z0-9]+)*$/i.test(name)) {
|
|
47
|
-
return { collection, name, format: 'colon' }
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return null
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function transformerIconHighlight(options: TransformerIconHighlightOptions = {}): ShikiTransformer {
|
|
55
|
-
const { htmlIcon } = options
|
|
56
|
-
|
|
57
|
-
return {
|
|
58
|
-
name: 'shiki-transformer-icon-highlight',
|
|
59
|
-
span(hast, _line, _col, _lineElement, token) {
|
|
60
|
-
const text = token.content
|
|
61
|
-
|
|
62
|
-
// Try to parse as an icon
|
|
63
|
-
const parsed = parseIconName(text)
|
|
64
|
-
if (!parsed) return
|
|
65
|
-
|
|
66
|
-
const iconIdentifier = `${parsed.collection}:${parsed.name}`
|
|
67
|
-
// Add color=black for mask-image to work properly (mask uses luminance)
|
|
68
|
-
const iconUrl = `https://api.iconify.design/${iconIdentifier}.svg?color=%23000`
|
|
69
|
-
|
|
70
|
-
// Create the icon element as a proper HAST element
|
|
71
|
-
const iconElement = htmlIcon
|
|
72
|
-
? { type: 'raw' as const, value: htmlIcon(iconIdentifier) }
|
|
73
|
-
: {
|
|
74
|
-
type: 'element' as const,
|
|
75
|
-
tagName: 'i',
|
|
76
|
-
properties: {
|
|
77
|
-
class: 'shiki-icon-highlight',
|
|
78
|
-
style: `--shiki-icon-url: url(${iconUrl})`
|
|
79
|
-
},
|
|
80
|
-
children: []
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Prepend the icon to the span content
|
|
84
|
-
if (hast.children) {
|
|
85
|
-
hast.children.unshift(iconElement)
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|