@movk/nuxt-docs 1.13.1 → 1.14.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.
- package/app/app.config.ts +6 -4
- package/app/assets/css/main.css +16 -0
- package/app/assets/icons/LICENSE +14 -0
- package/app/assets/icons/ai.svg +1 -0
- package/app/components/OgImage/Nuxt.vue +2 -4
- package/app/components/content/CommitChangelog.vue +8 -3
- package/app/components/content/ComponentEmits.vue +1 -1
- package/app/components/content/ComponentExample.vue +98 -72
- package/app/components/content/ComponentProps.vue +3 -3
- package/app/components/content/ComponentPropsSchema.vue +1 -1
- package/app/components/content/ComponentSlots.vue +1 -1
- package/app/components/content/HighlightInlineType.vue +1 -1
- package/app/components/content/PageLastCommit.vue +6 -5
- package/app/components/header/HeaderLogo.vue +1 -1
- package/app/composables/cachedParseMarkdown.ts +12 -0
- package/app/composables/fetchComponentExample.ts +5 -22
- package/app/composables/fetchComponentMeta.ts +5 -22
- package/app/mdc.config.ts +12 -0
- package/app/pages/docs/[...slug].vue +8 -2
- package/app/templates/releases.vue +3 -1
- package/app/types/index.d.ts +1 -1
- package/app/utils/shiki-transformer-icon-highlight.ts +89 -0
- package/app/workers/prettier.js +26 -17
- package/modules/ai-chat/index.ts +1 -1
- package/modules/ai-chat/runtime/components/AiChatModelSelect.vue +2 -1
- package/modules/component-example.ts +65 -30
- package/modules/config.ts +24 -1
- package/modules/css.ts +1 -1
- package/nuxt.config.ts +40 -2
- package/nuxt.schema.ts +4 -4
- package/package.json +17 -17
- package/server/api/component-example.get.ts +5 -5
- package/server/api/github/{commits.get.ts → commits.json.get.ts} +7 -4
- package/server/api/github/{last-commit.get.ts → last-commit.json.get.ts} +12 -9
- package/server/api/github/releases.json.get.ts +2 -2
- package/server/mcp/resources/documentation-pages.ts +26 -0
- package/server/mcp/resources/examples.ts +17 -0
- package/server/mcp/tools/get-example.ts +1 -1
- package/server/mcp/tools/list-examples.ts +4 -8
- package/server/mcp/tools/list-getting-started-guides.ts +29 -0
- package/server/routes/raw/[...slug].md.get.ts +3 -5
- package/server/utils/stringifyMinimark.ts +345 -0
- package/server/utils/transformMDC.ts +14 -5
- package/utils/meta.ts +1 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { ShikiTransformer } from '@shikijs/core'
|
|
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
|
+
}
|
package/app/workers/prettier.js
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
let _prettier
|
|
2
|
+
let _plugins
|
|
3
|
+
|
|
2
4
|
self.onmessage = async function (event) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
try {
|
|
6
|
+
self.postMessage({
|
|
7
|
+
uid: event.data.uid,
|
|
8
|
+
message: await handleMessage(event.data.message)
|
|
9
|
+
})
|
|
10
|
+
} catch (error) {
|
|
11
|
+
self.postMessage({
|
|
12
|
+
uid: event.data.uid,
|
|
13
|
+
error: error.message || String(error)
|
|
14
|
+
})
|
|
15
|
+
}
|
|
7
16
|
}
|
|
8
17
|
|
|
9
18
|
function handleMessage(message) {
|
|
@@ -14,23 +23,23 @@ function handleMessage(message) {
|
|
|
14
23
|
}
|
|
15
24
|
|
|
16
25
|
async function handleFormatMessage(message) {
|
|
17
|
-
if (!
|
|
18
|
-
await Promise.all([
|
|
19
|
-
import('https://cdn.jsdelivr.net/npm/prettier@3.7.4/standalone.
|
|
20
|
-
import('https://cdn.jsdelivr.net/npm/prettier@3.7.4/plugins/babel.
|
|
21
|
-
import('https://cdn.jsdelivr.net/npm/prettier@3.7.4/plugins/estree.
|
|
22
|
-
import('https://cdn.jsdelivr.net/npm/prettier@3.7.4/plugins/html.
|
|
23
|
-
import('https://cdn.jsdelivr.net/npm/prettier@3.7.4/plugins/markdown.
|
|
24
|
-
import('https://cdn.jsdelivr.net/npm/prettier@3.7.4/plugins/typescript.
|
|
26
|
+
if (!_prettier) {
|
|
27
|
+
const [prettierModule, ...plugins] = await Promise.all([
|
|
28
|
+
import('https://cdn.jsdelivr.net/npm/prettier@3.7.4/standalone.mjs'),
|
|
29
|
+
import('https://cdn.jsdelivr.net/npm/prettier@3.7.4/plugins/babel.mjs'),
|
|
30
|
+
import('https://cdn.jsdelivr.net/npm/prettier@3.7.4/plugins/estree.mjs'),
|
|
31
|
+
import('https://cdn.jsdelivr.net/npm/prettier@3.7.4/plugins/html.mjs'),
|
|
32
|
+
import('https://cdn.jsdelivr.net/npm/prettier@3.7.4/plugins/markdown.mjs'),
|
|
33
|
+
import('https://cdn.jsdelivr.net/npm/prettier@3.7.4/plugins/typescript.mjs')
|
|
25
34
|
])
|
|
35
|
+
_prettier = prettierModule
|
|
36
|
+
_plugins = plugins
|
|
26
37
|
}
|
|
27
38
|
|
|
28
39
|
const { options, source } = message
|
|
29
|
-
|
|
40
|
+
return _prettier.format(source, {
|
|
30
41
|
parser: 'markdown',
|
|
31
|
-
plugins:
|
|
42
|
+
plugins: _plugins,
|
|
32
43
|
...options
|
|
33
44
|
})
|
|
34
|
-
|
|
35
|
-
return formatted
|
|
36
45
|
}
|
package/modules/ai-chat/index.ts
CHANGED
|
@@ -20,7 +20,8 @@ const items = computed(() => models.map(model => ({
|
|
|
20
20
|
value-key="value"
|
|
21
21
|
class="hover:bg-default focus:bg-default data-[state=open]:bg-default"
|
|
22
22
|
:ui="{
|
|
23
|
-
trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
|
|
23
|
+
trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200',
|
|
24
|
+
content: 'w-auto min-w-(--reka-combobox-trigger-width)'
|
|
24
25
|
}"
|
|
25
26
|
/>
|
|
26
27
|
</template>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs'
|
|
2
2
|
import fsp from 'node:fs/promises'
|
|
3
|
-
import {
|
|
4
|
-
import { defineNuxtModule,
|
|
3
|
+
import { join } from 'pathe'
|
|
4
|
+
import { defineNuxtModule, addServerHandler, createResolver } from '@nuxt/kit'
|
|
5
5
|
|
|
6
6
|
export default defineNuxtModule({
|
|
7
7
|
meta: {
|
|
@@ -11,13 +11,20 @@ export default defineNuxtModule({
|
|
|
11
11
|
const resolver = createResolver(import.meta.url)
|
|
12
12
|
let _configResolved: any
|
|
13
13
|
let components: Record<string, any>
|
|
14
|
-
const
|
|
14
|
+
const outputDir = join(nuxt.options.buildDir, 'component-examples')
|
|
15
|
+
|
|
16
|
+
async function ensureOutputDir() {
|
|
17
|
+
if (!existsSync(outputDir)) {
|
|
18
|
+
await fsp.mkdir(outputDir, { recursive: true })
|
|
19
|
+
}
|
|
20
|
+
}
|
|
15
21
|
|
|
16
22
|
async function stubOutput() {
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
await ensureOutputDir()
|
|
24
|
+
const indexPath = join(outputDir, '_index.json')
|
|
25
|
+
if (!existsSync(indexPath)) {
|
|
26
|
+
await fsp.writeFile(indexPath, '[]', 'utf-8')
|
|
19
27
|
}
|
|
20
|
-
await updateOutput('export default {}')
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
async function fetchComponent(component: string | any) {
|
|
@@ -46,20 +53,26 @@ export default defineNuxtModule({
|
|
|
46
53
|
pascalName: component.pascalName
|
|
47
54
|
}
|
|
48
55
|
}
|
|
49
|
-
const getStringifiedComponents = () => JSON.stringify(components, null, 2)
|
|
50
56
|
|
|
51
|
-
|
|
52
|
-
|
|
57
|
+
async function writeComponentFile(name: string) {
|
|
58
|
+
const comp = components[name]
|
|
59
|
+
if (!comp?.code) return
|
|
60
|
+
await fsp.writeFile(
|
|
61
|
+
join(outputDir, `${name}.json`),
|
|
62
|
+
JSON.stringify({ code: comp.code, filePath: comp.filePath, pascalName: comp.pascalName }),
|
|
63
|
+
'utf-8'
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function writeIndex() {
|
|
68
|
+
const names = Object.keys(components).filter(k => components[k]?.code)
|
|
69
|
+
await fsp.writeFile(join(outputDir, '_index.json'), JSON.stringify(names), 'utf-8')
|
|
70
|
+
}
|
|
53
71
|
|
|
54
|
-
async function updateOutput(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
if (existsSync(path)) {
|
|
60
|
-
await fsp.unlink(path)
|
|
61
|
-
}
|
|
62
|
-
await fsp.writeFile(path, content || getVirtualModuleContent(), 'utf-8')
|
|
72
|
+
async function updateOutput() {
|
|
73
|
+
await ensureOutputDir()
|
|
74
|
+
await Promise.all(Object.keys(components).map(writeComponentFile))
|
|
75
|
+
await writeIndex()
|
|
63
76
|
}
|
|
64
77
|
|
|
65
78
|
async function fetchComponents() {
|
|
@@ -76,12 +89,6 @@ export default defineNuxtModule({
|
|
|
76
89
|
await stubOutput()
|
|
77
90
|
})
|
|
78
91
|
|
|
79
|
-
addTemplate({
|
|
80
|
-
filename: 'component-example.mjs',
|
|
81
|
-
getContents: () => 'export default {}',
|
|
82
|
-
write: true
|
|
83
|
-
})
|
|
84
|
-
|
|
85
92
|
nuxt.hook('vite:extend', (vite: any) => {
|
|
86
93
|
vite.config.plugins = vite.config.plugins || []
|
|
87
94
|
vite.config.plugins.push({
|
|
@@ -104,19 +111,47 @@ export default defineNuxtModule({
|
|
|
104
111
|
)
|
|
105
112
|
) {
|
|
106
113
|
await fetchComponent(file)
|
|
107
|
-
|
|
114
|
+
const entry = Object.entries(components).find(
|
|
115
|
+
([, comp]: any) => comp.filePath === file
|
|
116
|
+
)
|
|
117
|
+
if (entry) {
|
|
118
|
+
await ensureOutputDir()
|
|
119
|
+
await writeComponentFile(entry[0])
|
|
120
|
+
await writeIndex()
|
|
121
|
+
}
|
|
108
122
|
}
|
|
109
123
|
}
|
|
110
124
|
})
|
|
111
125
|
})
|
|
112
126
|
|
|
113
127
|
nuxt.hook('nitro:config', (nitroConfig) => {
|
|
128
|
+
nitroConfig.serverAssets = nitroConfig.serverAssets || []
|
|
129
|
+
nitroConfig.serverAssets.push({
|
|
130
|
+
baseName: 'component-examples',
|
|
131
|
+
dir: outputDir
|
|
132
|
+
})
|
|
133
|
+
|
|
114
134
|
nitroConfig.virtual = nitroConfig.virtual || {}
|
|
115
|
-
nitroConfig.virtual['#component-example/nitro'] = () =>
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
'utf-8'
|
|
119
|
-
|
|
135
|
+
nitroConfig.virtual['#component-example/nitro'] = () => {
|
|
136
|
+
const indexPath = join(outputDir, '_index.json')
|
|
137
|
+
const names: string[] = existsSync(indexPath)
|
|
138
|
+
? JSON.parse(readFileSync(indexPath, 'utf-8'))
|
|
139
|
+
: []
|
|
140
|
+
|
|
141
|
+
return `import { useStorage } from '#imports'
|
|
142
|
+
|
|
143
|
+
const names = ${JSON.stringify(names)}
|
|
144
|
+
const _storage = () => useStorage('assets:component-examples')
|
|
145
|
+
|
|
146
|
+
export async function getComponentExample(name) {
|
|
147
|
+
return await _storage().getItem(name + '.json')
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export async function listComponentExamples() {
|
|
151
|
+
return names
|
|
152
|
+
}
|
|
153
|
+
`
|
|
154
|
+
}
|
|
120
155
|
})
|
|
121
156
|
|
|
122
157
|
addServerHandler({
|
package/modules/config.ts
CHANGED
|
@@ -1,9 +1,27 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
1
2
|
import { createResolver, defineNuxtModule } from '@nuxt/kit'
|
|
2
3
|
import { defu } from 'defu'
|
|
4
|
+
import { join } from 'pathe'
|
|
3
5
|
import { getGitBranch, getGitEnv, getLocalGitInfo } from '../utils/git'
|
|
4
6
|
import { getPackageJsonMetadata, inferSiteURL } from '../utils/meta'
|
|
5
7
|
import { createComponentMetaExcludeFilters } from '../utils/component-meta'
|
|
6
8
|
|
|
9
|
+
function getMdcConfigSources(nuxt: any): string[] {
|
|
10
|
+
return nuxt.options._layers.flatMap((layer: any) => {
|
|
11
|
+
const tsConfigPath = join(layer.config.srcDir, 'mdc.config.ts')
|
|
12
|
+
if (existsSync(tsConfigPath)) {
|
|
13
|
+
return [tsConfigPath]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const jsConfigPath = join(layer.config.srcDir, 'mdc.config.js')
|
|
17
|
+
if (existsSync(jsConfigPath)) {
|
|
18
|
+
return [jsConfigPath]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return []
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
7
25
|
export default defineNuxtModule({
|
|
8
26
|
meta: {
|
|
9
27
|
name: 'config'
|
|
@@ -31,7 +49,6 @@ export default defineNuxtModule({
|
|
|
31
49
|
domain: url || 'https://example.com',
|
|
32
50
|
title: siteName,
|
|
33
51
|
description: meta.description || '',
|
|
34
|
-
contentRawMarkdown: false as const,
|
|
35
52
|
full: {
|
|
36
53
|
title: siteName,
|
|
37
54
|
description: meta.description || ''
|
|
@@ -73,5 +90,11 @@ export default defineNuxtModule({
|
|
|
73
90
|
...createComponentMetaExcludeFilters(resolve, dir, layerPath, userInclude)
|
|
74
91
|
]
|
|
75
92
|
})
|
|
93
|
+
|
|
94
|
+
// Load mdc.config from all layers
|
|
95
|
+
const mdcConfigSources = getMdcConfigSources(nuxt)
|
|
96
|
+
if (mdcConfigSources.length > 0) {
|
|
97
|
+
await nuxt.callHook('mdc:configSources', mdcConfigSources)
|
|
98
|
+
}
|
|
76
99
|
}
|
|
77
100
|
})
|
package/modules/css.ts
CHANGED
package/nuxt.config.ts
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import { defineNuxtConfig } from 'nuxt/config'
|
|
2
2
|
import pkg from './package.json'
|
|
3
|
+
import { createResolver, useNuxt } from '@nuxt/kit'
|
|
4
|
+
import { join } from 'pathe'
|
|
5
|
+
|
|
6
|
+
const { resolve } = createResolver(import.meta.url)
|
|
3
7
|
|
|
4
8
|
export default defineNuxtConfig({
|
|
5
9
|
modules: [
|
|
10
|
+
() => {
|
|
11
|
+
const nuxt = useNuxt()
|
|
12
|
+
nuxt.options.icon ||= {}
|
|
13
|
+
nuxt.options.icon.customCollections ||= []
|
|
14
|
+
nuxt.options.icon.customCollections.push({
|
|
15
|
+
prefix: 'custom',
|
|
16
|
+
dir: join(nuxt.options.srcDir, 'assets/icons')
|
|
17
|
+
})
|
|
18
|
+
},
|
|
6
19
|
'@nuxt/ui',
|
|
7
20
|
'@nuxt/content',
|
|
8
21
|
'@nuxt/image',
|
|
@@ -24,6 +37,7 @@ export default defineNuxtConfig({
|
|
|
24
37
|
},
|
|
25
38
|
|
|
26
39
|
content: {
|
|
40
|
+
experimental: { sqliteConnector: 'native' },
|
|
27
41
|
build: {
|
|
28
42
|
markdown: {
|
|
29
43
|
highlight: {
|
|
@@ -46,6 +60,15 @@ export default defineNuxtConfig({
|
|
|
46
60
|
}
|
|
47
61
|
},
|
|
48
62
|
|
|
63
|
+
ui: {
|
|
64
|
+
theme: {
|
|
65
|
+
colors: ['primary', 'secondary', 'info', 'success', 'warning', 'error', 'important']
|
|
66
|
+
},
|
|
67
|
+
experimental: {
|
|
68
|
+
componentDetection: true
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
|
|
49
72
|
runtimeConfig: {
|
|
50
73
|
public: {
|
|
51
74
|
version: pkg.version
|
|
@@ -61,7 +84,7 @@ export default defineNuxtConfig({
|
|
|
61
84
|
}
|
|
62
85
|
},
|
|
63
86
|
|
|
64
|
-
compatibilityDate: '
|
|
87
|
+
compatibilityDate: '2026-01-14',
|
|
65
88
|
|
|
66
89
|
nitro: {
|
|
67
90
|
prerender: {
|
|
@@ -143,7 +166,22 @@ export default defineNuxtConfig({
|
|
|
143
166
|
},
|
|
144
167
|
|
|
145
168
|
icon: {
|
|
146
|
-
|
|
169
|
+
customCollections: [
|
|
170
|
+
{
|
|
171
|
+
prefix: 'custom',
|
|
172
|
+
dir: resolve('./app/assets/icons')
|
|
173
|
+
}
|
|
174
|
+
],
|
|
175
|
+
clientBundle: {
|
|
176
|
+
scan: true,
|
|
177
|
+
includeCustomCollections: true
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
llms: {
|
|
182
|
+
// Must be defined before @nuxt/content setup,
|
|
183
|
+
// otherwise Content LLMS module will overwrite it in modules:done.
|
|
184
|
+
contentRawMarkdown: false
|
|
147
185
|
},
|
|
148
186
|
|
|
149
187
|
ogImage: {
|
package/nuxt.schema.ts
CHANGED
|
@@ -315,7 +315,7 @@ export default defineNuxtSchema({
|
|
|
315
315
|
aiChat: group({
|
|
316
316
|
title: 'AI Chat',
|
|
317
317
|
description: 'AI 聊天助手配置',
|
|
318
|
-
icon: 'i-
|
|
318
|
+
icon: 'i-custom-ai',
|
|
319
319
|
fields: {
|
|
320
320
|
floatingInput: field({
|
|
321
321
|
type: 'boolean',
|
|
@@ -440,7 +440,7 @@ export default defineNuxtSchema({
|
|
|
440
440
|
type: 'string',
|
|
441
441
|
title: '触发按钮',
|
|
442
442
|
description: 'AI 聊天面板触发按钮的提示文本',
|
|
443
|
-
icon: 'i-
|
|
443
|
+
icon: 'i-custom-ai',
|
|
444
444
|
default: '与 AI 聊天'
|
|
445
445
|
}),
|
|
446
446
|
streaming: field({
|
|
@@ -483,8 +483,8 @@ export default defineNuxtSchema({
|
|
|
483
483
|
type: 'string',
|
|
484
484
|
title: '触发图标',
|
|
485
485
|
description: 'AI 聊天触发按钮的图标',
|
|
486
|
-
icon: 'i-
|
|
487
|
-
default: 'i-
|
|
486
|
+
icon: 'i-custom-ai',
|
|
487
|
+
default: 'i-custom-ai'
|
|
488
488
|
}),
|
|
489
489
|
explain: field({
|
|
490
490
|
type: 'string',
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@movk/nuxt-docs",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.14.1",
|
|
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>",
|
|
@@ -39,36 +39,35 @@
|
|
|
39
39
|
"README.md"
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@ai-sdk/gateway": "^3.0.
|
|
43
|
-
"@ai-sdk/mcp": "^1.0.
|
|
44
|
-
"@ai-sdk/vue": "^3.0.
|
|
45
|
-
"@iconify-json/lucide": "^1.2.
|
|
46
|
-
"@iconify-json/simple-icons": "^1.2.
|
|
42
|
+
"@ai-sdk/gateway": "^3.0.66",
|
|
43
|
+
"@ai-sdk/mcp": "^1.0.25",
|
|
44
|
+
"@ai-sdk/vue": "^3.0.116",
|
|
45
|
+
"@iconify-json/lucide": "^1.2.95",
|
|
46
|
+
"@iconify-json/simple-icons": "^1.2.72",
|
|
47
47
|
"@iconify-json/vscode-icons": "^1.2.44",
|
|
48
|
-
"@movk/core": "^1.
|
|
48
|
+
"@movk/core": "^1.2.0",
|
|
49
49
|
"@nuxt/a11y": "^1.0.0-alpha.1",
|
|
50
|
-
"@nuxt/content": "^3.
|
|
50
|
+
"@nuxt/content": "^3.12.0",
|
|
51
51
|
"@nuxt/image": "^2.0.0",
|
|
52
52
|
"@nuxt/kit": "^4.3.1",
|
|
53
|
-
"@nuxt/ui": "^4.5.
|
|
53
|
+
"@nuxt/ui": "^4.5.1",
|
|
54
54
|
"@nuxtjs/mcp-toolkit": "^0.7.0",
|
|
55
|
-
"@nuxtjs/mdc": "^0.20.
|
|
56
|
-
"@nuxtjs/robots": "^5.7.
|
|
55
|
+
"@nuxtjs/mdc": "^0.20.2",
|
|
56
|
+
"@nuxtjs/robots": "^5.7.1",
|
|
57
57
|
"@octokit/rest": "^22.0.1",
|
|
58
58
|
"@openrouter/ai-sdk-provider": "^2.2.3",
|
|
59
|
-
"@shikijs/core": "^
|
|
60
|
-
"@shikijs/engine-javascript": "^
|
|
61
|
-
"@shikijs/langs": "^
|
|
62
|
-
"@shikijs/themes": "^
|
|
59
|
+
"@shikijs/core": "^4.0.1",
|
|
60
|
+
"@shikijs/engine-javascript": "^4.0.1",
|
|
61
|
+
"@shikijs/langs": "^4.0.1",
|
|
62
|
+
"@shikijs/themes": "^4.0.1",
|
|
63
63
|
"@vercel/analytics": "^1.6.1",
|
|
64
64
|
"@vercel/speed-insights": "^1.3.1",
|
|
65
65
|
"@vueuse/core": "^14.2.1",
|
|
66
66
|
"@vueuse/nuxt": "^14.2.1",
|
|
67
|
-
"ai": "^6.0.
|
|
67
|
+
"ai": "^6.0.111",
|
|
68
68
|
"defu": "^6.1.4",
|
|
69
69
|
"exsolve": "^1.0.8",
|
|
70
70
|
"git-url-parse": "^16.1.0",
|
|
71
|
-
"minimark": "^1.0.0",
|
|
72
71
|
"motion-v": "^2.0.0",
|
|
73
72
|
"nuxt": "^4.3.1",
|
|
74
73
|
"nuxt-component-meta": "^0.17.2",
|
|
@@ -79,6 +78,7 @@
|
|
|
79
78
|
"pkg-types": "^2.3.0",
|
|
80
79
|
"prettier": "^3.8.1",
|
|
81
80
|
"scule": "^1.3.0",
|
|
81
|
+
"shiki-transformer-color-highlight": "^1.1.0",
|
|
82
82
|
"shiki-stream": "^0.1.4",
|
|
83
83
|
"tailwind-merge": "^3.5.0",
|
|
84
84
|
"tailwindcss": "^4.2.1",
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { defineEventHandler, createError, appendHeader } from 'h3'
|
|
2
2
|
import { pascalCase } from 'scule'
|
|
3
3
|
// @ts-expect-error - no types available
|
|
4
|
-
import
|
|
4
|
+
import { getComponentExample } from '#component-example/nitro'
|
|
5
5
|
|
|
6
|
-
export default defineEventHandler((event) => {
|
|
6
|
+
export default defineEventHandler(async (event) => {
|
|
7
7
|
appendHeader(event, 'Access-Control-Allow-Origin', '*')
|
|
8
8
|
const componentName = (event.context.params?.['component?'] || '').replace(/\.json$/, '')
|
|
9
9
|
if (componentName) {
|
|
10
|
-
const component =
|
|
10
|
+
const component = await getComponentExample(pascalCase(componentName))
|
|
11
11
|
if (!component) {
|
|
12
12
|
throw createError({
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
statusMessage: 'Example not found!',
|
|
14
|
+
statusCode: 404
|
|
15
15
|
})
|
|
16
16
|
}
|
|
17
17
|
return component
|
|
@@ -24,20 +24,23 @@ export default defineCachedEventHandler(async (event) => {
|
|
|
24
24
|
})
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
const octokit = new Octokit({
|
|
27
|
+
const octokit = new Octokit({
|
|
28
|
+
auth: process.env.NUXT_GITHUB_TOKEN,
|
|
29
|
+
request: { timeout: 10_000 }
|
|
30
|
+
})
|
|
28
31
|
|
|
29
32
|
const allCommits = await Promise.all(
|
|
30
33
|
paths.map(path =>
|
|
31
|
-
octokit.
|
|
34
|
+
octokit.rest.repos.listCommits({
|
|
32
35
|
sha: github.branch,
|
|
33
36
|
owner: github.owner,
|
|
34
37
|
repo: github.name,
|
|
35
38
|
path,
|
|
36
39
|
since: github.since,
|
|
37
|
-
per_page: github.per_page,
|
|
40
|
+
per_page: github.per_page || 100,
|
|
38
41
|
until: github.until,
|
|
39
42
|
author
|
|
40
|
-
})
|
|
43
|
+
}).then(res => res.data).catch(() => [])
|
|
41
44
|
)
|
|
42
45
|
)
|
|
43
46
|
|
|
@@ -22,16 +22,19 @@ export default defineCachedEventHandler(async (event) => {
|
|
|
22
22
|
})
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const octokit = new Octokit({
|
|
25
|
+
const octokit = new Octokit({
|
|
26
|
+
auth: process.env.NUXT_GITHUB_TOKEN,
|
|
27
|
+
request: { timeout: 10_000 }
|
|
28
|
+
})
|
|
26
29
|
|
|
27
30
|
try {
|
|
28
|
-
const
|
|
31
|
+
const commits = await octokit.rest.repos.listCommits({
|
|
29
32
|
sha: github.branch,
|
|
30
33
|
owner: github.owner,
|
|
31
34
|
repo: github.name,
|
|
32
35
|
path,
|
|
33
36
|
per_page: 1
|
|
34
|
-
})
|
|
37
|
+
}).then(res => res.data).catch(() => [])
|
|
35
38
|
|
|
36
39
|
if (!commits.length) {
|
|
37
40
|
return null
|
|
@@ -54,16 +57,16 @@ export default defineCachedEventHandler(async (event) => {
|
|
|
54
57
|
// 从 squash commit message 中提取 PR 编号 (#166)
|
|
55
58
|
const prMatch = commit.commit.message.match(/#(\d+)/)
|
|
56
59
|
if (prMatch?.[1]) {
|
|
57
|
-
const
|
|
60
|
+
const prData = await octokit.rest.pulls.get({
|
|
58
61
|
owner: github.owner,
|
|
59
62
|
repo: github.name,
|
|
60
63
|
pull_number: Number.parseInt(prMatch[1])
|
|
61
|
-
})
|
|
64
|
+
}).then(res => res.data).catch(() => null)
|
|
62
65
|
|
|
63
|
-
authorLogin = prData
|
|
64
|
-
authorAvatar = prData
|
|
65
|
-
authorName = prData
|
|
66
|
-
commitUrl = prData
|
|
66
|
+
authorLogin = prData?.user?.login ?? authorLogin
|
|
67
|
+
authorAvatar = prData?.user?.avatar_url ?? authorAvatar
|
|
68
|
+
authorName = prData?.user?.name || authorLogin
|
|
69
|
+
commitUrl = prData?.html_url ?? commitUrl
|
|
67
70
|
}
|
|
68
71
|
} catch {
|
|
69
72
|
// 获取 PR 信息失败时忽略,使用原始提交者信息
|
|
@@ -16,10 +16,10 @@ export default defineCachedEventHandler(async () => {
|
|
|
16
16
|
|
|
17
17
|
const octokit = new Octokit({ auth: process.env.NUXT_GITHUB_TOKEN })
|
|
18
18
|
|
|
19
|
-
const
|
|
19
|
+
const releases = await octokit.rest.repos.listReleases({
|
|
20
20
|
owner: github.owner,
|
|
21
21
|
repo: github.name
|
|
22
|
-
})
|
|
22
|
+
}).then(res => res.data).catch(() => [])
|
|
23
23
|
|
|
24
24
|
return releases
|
|
25
25
|
}, {
|