diff --git a/app/app.vue b/app/app.vue
index 0c05b133b..9694cd532 100644
--- a/app/app.vue
+++ b/app/app.vue
@@ -3,7 +3,6 @@ const colorMode = useColorMode()
const route = useRoute()
const isChatRoute = computed(() => route.path === '/chat' || route.path.startsWith('/chat/'))
const { version } = useDocsVersion()
-const { searchGroups, searchLinks, searchTerm, searchFuse } = useNavigation()
const { fetchList: fetchModules } = useModules()
const { fetchList: fetchHosting } = useHostingProviders()
const { track } = useAnalytics()
@@ -16,10 +15,7 @@ watch(() => colorMode.preference, (newMode, oldMode) => {
}
})
-const [{ data: navigation }, { data: files }] = await Promise.all([
- useFetch('/api/navigation.json'),
- useFetch('/api/search.json', { server: false })
-])
+const { data: navigation } = await useFetch('/api/navigation.json')
onNuxtReady(() => {
fetchModules()
@@ -64,9 +60,6 @@ if (import.meta.server) {
}
const versionNavigation = computed(() => navigation.value?.filter(item => item.path === version.value.path || item.path === '/blog') ?? [])
-const versionFiles = computed(() => files.value?.filter((file) => {
- return file.id.startsWith(version.value.path + '/') || file.id.startsWith('/blog/')
-}) ?? [])
provide('navigation', versionNavigation)
@@ -80,26 +73,16 @@ provide('navigation', versionNavigation)
-
-
-
-
-
-
+
+
+
-
+
diff --git a/app/components/Search.vue b/app/components/Search.vue
new file mode 100644
index 000000000..631cb309b
--- /dev/null
+++ b/app/components/Search.vue
@@ -0,0 +1,44 @@
+
+
+
+
+
diff --git a/app/composables/useNavigation.ts b/app/composables/useNavigation.ts
index 16401b44a..5b52cc1c4 100644
--- a/app/composables/useNavigation.ts
+++ b/app/composables/useNavigation.ts
@@ -4,7 +4,7 @@ import { createSharedComposable } from '@vueuse/core'
// Stable reference so the deep watcher inside `useFuse` doesn't rebuild
// the entire Fuse index on every reactive flush.
const searchFuse = {
- resultLimit: 42,
+ resultLimit: 25,
fuseOptions: {
threshold: 0
}
@@ -118,6 +118,7 @@ function _useHeaderLinks() {
}, {
label: 'Updates',
icon: 'i-lucide-newspaper',
+ search: false,
to: '/blog',
children: [{
label: 'Blog',
@@ -229,6 +230,7 @@ const _useNavigation = () => {
id: `module-${module.name}`,
label: module.npm,
suffix: module.description,
+ downloads: module.stats?.downloads ?? 0,
avatar: {
src: moduleImage(module.icon),
ui: {
@@ -255,6 +257,20 @@ const _useNavigation = () => {
})))
const searchGroups = computed(() => [{
+ id: 'modules-search',
+ label: 'Modules',
+ items: modulesItems.value,
+ postFilter: (searchTerm: string, items: any[]) => {
+ if (!searchTerm) {
+ return [...items].sort((a, b) => (b.downloads ?? 0) - (a.downloads ?? 0))
+ }
+ return items
+ }
+ }, {
+ id: 'hosting-search',
+ label: 'Hosting',
+ items: hostingItems.value
+ }, {
id: 'ask-ai-search',
label: 'AI',
ignoreFilter: true,
@@ -273,14 +289,6 @@ const _useNavigation = () => {
openAgent(searchTerm.value)
}
}]
- }, {
- id: 'modules-search',
- label: 'Modules',
- items: modulesItems.value
- }, {
- id: 'hosting-search',
- label: 'Hosting',
- items: hostingItems.value
}])
watchDebounced(searchTerm, (term) => {
diff --git a/app/error.vue b/app/error.vue
index f8e36eb02..975c391b8 100644
--- a/app/error.vue
+++ b/app/error.vue
@@ -10,14 +10,10 @@ defineProps<{ error: NuxtError }>()
const route = useRoute()
const { version } = useDocsVersion()
-const { searchGroups, searchLinks, searchTerm, searchFuse } = useNavigation()
const { fetchList: fetchModules } = useModules()
const { fetchList: fetchHosting } = useHostingProviders()
-const [{ data: navigation }, { data: files }] = await Promise.all([
- useFetch('/api/navigation.json'),
- useFetch('/api/search.json', { server: false })
-])
+const { data: navigation } = await useFetch('/api/navigation.json')
onNuxtReady(() => {
fetchModules()
@@ -25,9 +21,6 @@ onNuxtReady(() => {
})
const versionNavigation = computed(() => navigation.value?.filter(item => item.path === version.value.path || item.path === '/blog') ?? [])
-const versionFiles = computed(() => files.value?.filter((file) => {
- return file.id.startsWith(version.value.path + '/') || file.id.startsWith('/blog/')
-}) ?? [])
provide('navigation', versionNavigation)
@@ -42,14 +35,7 @@ provide('navigation', versionNavigation)
-
+
diff --git a/content/blog/.navigation.yml b/content/blog/.navigation.yml
new file mode 100644
index 000000000..eb9121c4a
--- /dev/null
+++ b/content/blog/.navigation.yml
@@ -0,0 +1 @@
+icon: i-lucide-newspaper
diff --git a/nuxt.config.ts b/nuxt.config.ts
index c6dccca47..de3d9cb47 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -169,7 +169,6 @@ export default defineNuxtConfig({
'/admin/**': { ssr: false },
// Main navigation
'/api/navigation.json': { prerender: true },
- '/api/search.json': { prerender: true },
// Redirects
'/docs': { redirect: '/docs/getting-started/introduction', prerender: false },
'/docs/3.x': { redirect: '/docs/3.x/getting-started/introduction', prerender: false },
diff --git a/package.json b/package.json
index ee1df56e1..302ec0a3a 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,7 @@
"@nuxt/hints": "^1.1.2",
"@nuxt/image": "^2.0.0",
"@nuxt/scripts": "^1.1.0",
- "@nuxt/ui": "^4.7.1",
+ "@nuxt/ui": "^4.8.0",
"@nuxthub/core": "^0.10.7",
"@nuxtjs/html-validator": "^2.1.0",
"@nuxtjs/mcp-toolkit": "^0.17.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 540d843ed..206beda82 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -54,8 +54,8 @@ importers:
specifier: ^1.1.0
version: 1.1.0(@types/youtube@0.2.0)(@unhead/vue@2.1.15(vue@3.5.34(typescript@6.0.3)))(@vercel/functions@3.6.0)(db0@0.3.4(@libsql/client@0.17.3)(better-sqlite3@12.10.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260511.1)(@libsql/client@0.17.3)(@opentelemetry/api@1.9.0)(better-sqlite3@12.10.0)))(ioredis@5.10.1)(magicast@0.5.3)(rolldown@1.0.0-beta.57(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(typescript@6.0.3)(vite@7.3.3(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.34(typescript@6.0.3))
'@nuxt/ui':
- specifier: ^4.7.1
- version: 4.7.1(2ea66ffd91c5bb6b00b990c832f16d94)
+ specifier: ^4.8.0
+ version: 4.8.0(2ea66ffd91c5bb6b00b990c832f16d94)
'@nuxthub/core':
specifier: ^0.10.7
version: 0.10.7(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@vercel/functions@3.6.0)(db0@0.3.4(@libsql/client@0.17.3)(better-sqlite3@12.10.0)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260511.1)(@libsql/client@0.17.3)(@opentelemetry/api@1.9.0)(better-sqlite3@12.10.0)))(ioredis@5.10.1)(magicast@0.5.3)(typescript@6.0.3)(vue-tsc@3.3.0(typescript@6.0.3))
@@ -1894,8 +1894,8 @@ packages:
vitest:
optional: true
- '@nuxt/ui@4.7.1':
- resolution: {integrity: sha512-s3Ix89RkJTeNDlLg7EflckkFxQgzm2W9bt4CBsudi7wNdmhbb3nzYG6rcns2R2Wos0gZlYkSfDKaX1o3zMC+Aw==}
+ '@nuxt/ui@4.8.0':
+ resolution: {integrity: sha512-ymnGxvMCYtPzuMGIK/fnd41/Arh9hxfmz9m5auaf51jg/zhSpAkIesTVSv0at+bGKXxwtQZoPS9GXs0+SzWfjg==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@@ -8484,8 +8484,8 @@ packages:
rehype-sort-attributes@5.0.1:
resolution: {integrity: sha512-Bxo+AKUIELcnnAZwJDt5zUDDRpt4uzhfz9d0PVGhcxYWsbFj5Cv35xuWxu5r1LeYNFNhgGqsr9Q2QiIOM/Qctg==}
- reka-ui@2.9.6:
- resolution: {integrity: sha512-K6bL457owpvWONc7hsjFxo3HDC9s6IzhRqShW0w9JSKelPGfRbkHD558UQTn/NH1cvrXVHygKyC7fExFmRketg==}
+ reka-ui@2.9.7:
+ resolution: {integrity: sha512-aX7foYYR20v4+majO58OJJdBNfLMm0eJb448l9N4JVy8JB7GXOr4H/S4a+J1pkcoxZH8Cb7YHpJ855+miAm7sA==}
peerDependencies:
vue: '>= 3.4.0'
@@ -9722,8 +9722,8 @@ packages:
typescript:
optional: true
- vue-component-type-helpers@3.2.8:
- resolution: {integrity: sha512-9689efAXhN/EV86plgkL/XFiJSXhGtWPG6JDboZ+QnjlUWUUQrQ0ILKQtw4iQsuwIwu5k6Aw+JnehDe7161e7A==}
+ vue-component-type-helpers@3.3.1:
+ resolution: {integrity: sha512-pu58kqxmVyEH6VfNYW1UyEfR3XAnJ27ZXT3yzXxxpjLxVzAbyC35Zk/nm/RMs7ijWnJNSd9fWkeex2OhUsx3MA==}
vue-demi@0.14.10:
resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
@@ -11883,7 +11883,7 @@ snapshots:
- typescript
- vite
- '@nuxt/ui@4.7.1(2ea66ffd91c5bb6b00b990c832f16d94)':
+ '@nuxt/ui@4.8.0(2ea66ffd91c5bb6b00b990c832f16d94)':
dependencies:
'@floating-ui/dom': 1.7.6
'@iconify/vue': 5.0.1(vue@3.5.34(typescript@6.0.3))
@@ -11936,7 +11936,7 @@ snapshots:
motion-v: 2.2.1(@vueuse/core@14.3.0(vue@3.5.34(typescript@6.0.3)))(vue@3.5.34(typescript@6.0.3))
ohash: 2.0.11
pathe: 2.0.3
- reka-ui: 2.9.6(vue@3.5.34(typescript@6.0.3))
+ reka-ui: 2.9.7(vue@3.5.34(typescript@6.0.3))
scule: 1.3.0
tailwind-merge: 3.6.0
tailwind-variants: 3.2.2(tailwind-merge@3.6.0)(tailwindcss@4.3.0)
@@ -11947,8 +11947,8 @@ snapshots:
unplugin: 3.0.0
unplugin-auto-import: 21.0.0(@nuxt/kit@4.4.6(magicast@0.5.3))(@vueuse/core@14.3.0(vue@3.5.34(typescript@6.0.3)))
unplugin-vue-components: 32.0.0(@nuxt/kit@4.4.6(magicast@0.5.3))(vue@3.5.34(typescript@6.0.3))
- vaul-vue: 0.4.1(reka-ui@2.9.6(vue@3.5.34(typescript@6.0.3)))(vue@3.5.34(typescript@6.0.3))
- vue-component-type-helpers: 3.2.8
+ vaul-vue: 0.4.1(reka-ui@2.9.7(vue@3.5.34(typescript@6.0.3)))(vue@3.5.34(typescript@6.0.3))
+ vue-component-type-helpers: 3.3.1
optionalDependencies:
'@internationalized/date': 3.12.1
'@internationalized/number': 3.6.6
@@ -14304,7 +14304,7 @@ snapshots:
'@vue/compiler-dom': 3.5.34
js-beautify: 1.15.4
vue: 3.5.34(typescript@6.0.3)
- vue-component-type-helpers: 3.2.8
+ vue-component-type-helpers: 3.3.1
optionalDependencies:
'@vue/server-renderer': 3.5.34(vue@3.5.34(typescript@6.0.3))
@@ -18412,7 +18412,7 @@ snapshots:
dependencies:
'@nuxt/devtools-kit': 4.0.0-alpha.3(magicast@0.5.3)(vite@7.3.3(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.9.0))
'@nuxt/kit': 4.4.6(magicast@0.5.3)
- '@nuxt/ui': 4.7.1(2ea66ffd91c5bb6b00b990c832f16d94)
+ '@nuxt/ui': 4.8.0(2ea66ffd91c5bb6b00b990c832f16d94)
'@shikijs/langs': 4.1.0
'@shikijs/themes': 4.1.0
'@vueuse/nuxt': 14.3.0(magicast@0.5.3)(nuxt@4.4.6(211797a271aa9149e05160704ba2ff9e))(vue@3.5.34(typescript@6.0.3))
@@ -19483,7 +19483,7 @@ snapshots:
'@types/hast': 3.0.4
unist-util-visit: 5.1.0
- reka-ui@2.9.6(vue@3.5.34(typescript@6.0.3)):
+ reka-ui@2.9.7(vue@3.5.34(typescript@6.0.3)):
dependencies:
'@floating-ui/dom': 1.7.6
'@floating-ui/vue': 1.1.11(vue@3.5.34(typescript@6.0.3))
@@ -20757,10 +20757,10 @@ snapshots:
vary@1.1.2: {}
- vaul-vue@0.4.1(reka-ui@2.9.6(vue@3.5.34(typescript@6.0.3)))(vue@3.5.34(typescript@6.0.3)):
+ vaul-vue@0.4.1(reka-ui@2.9.7(vue@3.5.34(typescript@6.0.3)))(vue@3.5.34(typescript@6.0.3)):
dependencies:
'@vueuse/core': 10.11.1(vue@3.5.34(typescript@6.0.3))
- reka-ui: 2.9.6(vue@3.5.34(typescript@6.0.3))
+ reka-ui: 2.9.7(vue@3.5.34(typescript@6.0.3))
vue: 3.5.34(typescript@6.0.3)
transitivePeerDependencies:
- '@vue/composition-api'
@@ -20958,7 +20958,7 @@ snapshots:
optionalDependencies:
typescript: 5.9.3
- vue-component-type-helpers@3.2.8: {}
+ vue-component-type-helpers@3.3.1: {}
vue-demi@0.14.10(vue@3.5.34(typescript@6.0.3)):
dependencies:
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 953be7278..aa0c4b4c9 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -11,3 +11,6 @@ allowBuilds:
puppeteer: false
unrs-resolver: false
vue-demi: false
+
+minimumReleaseAgeExclude:
+ - '@nuxt/ui@4.8.0'
diff --git a/server/api/search.json.get.ts b/server/api/search.json.get.ts
deleted file mode 100644
index 7dfe14f38..000000000
--- a/server/api/search.json.get.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-// This route will be pre-rendered as /api/navigation.json
-import { queryCollectionSearchSections } from '@nuxt/content/server'
-
-export default defineEventHandler(async (event) => {
- return Promise.all([
- queryCollectionSearchSections(event, 'docsv3', { ignoredTags: ['style'] }),
- queryCollectionSearchSections(event, 'docsv4', { ignoredTags: ['style'] }),
- queryCollectionSearchSections(event, 'docsv5', { ignoredTags: ['style'] }),
- queryCollectionSearchSections(event, 'blog')
- ]).then(data => data.flat())
-})