docus 5.5.1 → 5.6.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/app/app.vue CHANGED
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { ContentNavigationItem, PageCollections } from '@nuxt/content'
3
3
  import * as nuxtUiLocales from '@nuxt/ui/locale'
4
+ import { transformNavigation } from './utils/navigation'
4
5
 
5
6
  const { seo } = useAppConfig()
6
7
  const site = useSiteConfig()
@@ -45,11 +46,7 @@ if (isEnabled.value) {
45
46
  }
46
47
 
47
48
  const { data: navigation } = await useAsyncData(() => `navigation_${collectionName.value}`, () => queryCollectionNavigation(collectionName.value as keyof PageCollections), {
48
- transform: (data: ContentNavigationItem[]) => {
49
- const rootResult = data.find(item => item.path === '/docs')?.children || data || []
50
-
51
- return rootResult.find((item: ContentNavigationItem) => item.path === `/${locale.value}`)?.children || rootResult
52
- },
49
+ transform: (data: ContentNavigationItem[]) => transformNavigation(data, isEnabled.value, locale.value),
53
50
  watch: [locale],
54
51
  })
55
52
  const { data: files } = useLazyAsyncData(`search_${collectionName.value}`, () => queryCollectionSearchSections(collectionName.value as keyof PageCollections), {
package/app/error.vue CHANGED
@@ -2,6 +2,7 @@
2
2
  import type { NuxtError } from '#app'
3
3
  import type { ContentNavigationItem, PageCollections } from '@nuxt/content'
4
4
  import * as nuxtUiLocales from '@nuxt/ui/locale'
5
+ import { transformNavigation } from './utils/navigation'
5
6
 
6
7
  const props = defineProps<{
7
8
  error: NuxtError
@@ -47,11 +48,7 @@ if (isEnabled.value) {
47
48
  const collectionName = computed(() => isEnabled.value ? `docs_${locale.value}` : 'docs')
48
49
 
49
50
  const { data: navigation } = await useAsyncData(`navigation_${collectionName.value}`, () => queryCollectionNavigation(collectionName.value as keyof PageCollections), {
50
- transform: (data: ContentNavigationItem[]) => {
51
- const rootResult = data.find(item => item.path === '/docs')?.children || data || []
52
-
53
- return rootResult.find(item => item.path === `/${locale.value}`)?.children || rootResult
54
- },
51
+ transform: (data: ContentNavigationItem[]) => transformNavigation(data, isEnabled.value, locale.value),
55
52
  watch: [locale],
56
53
  })
57
54
  const { data: files } = useLazyAsyncData(`search_${collectionName.value}`, () => queryCollectionSearchSections(collectionName.value as keyof PageCollections), {
@@ -6,6 +6,25 @@ export const flattenNavigation = (items?: ContentNavigationItem[]): ContentNavig
6
6
  : [item],
7
7
  ) || []
8
8
 
9
+ /**
10
+ * Transform navigation data by stripping locale and docs levels
11
+ */
12
+ export function transformNavigation(
13
+ data: ContentNavigationItem[],
14
+ isI18nEnabled: boolean,
15
+ locale?: string,
16
+ ): ContentNavigationItem[] {
17
+ if (isI18nEnabled && locale) {
18
+ // i18n: first strip locale level, then check for docs level
19
+ const localeResult = data.find(item => item.path === `/${locale}`)?.children || data
20
+ return localeResult.find(item => item.path === `/${locale}/docs`)?.children || localeResult
21
+ }
22
+ else {
23
+ // non-i18n: strip docs level if exists
24
+ return data.find(item => item.path === '/docs')?.children || data
25
+ }
26
+ }
27
+
9
28
  export interface BreadcrumbItem {
10
29
  title: string
11
30
  path: string
package/content.config.ts CHANGED
@@ -2,11 +2,15 @@ import type { DefinedCollection } from '@nuxt/content'
2
2
  import { defineContentConfig, defineCollection, z } from '@nuxt/content'
3
3
  import { useNuxt } from '@nuxt/kit'
4
4
  import { joinURL } from 'ufo'
5
+ import { landingPageExists, docsFolderExists } from './utils/pages'
5
6
 
6
7
  const { options } = useNuxt()
7
8
  const cwd = joinURL(options.rootDir, 'content')
8
9
  const locales = options.i18n?.locales
9
10
 
11
+ const hasLandingPage = landingPageExists(options.rootDir)
12
+ const hasDocsFolder = docsFolderExists(options.rootDir)
13
+
10
14
  const createDocsSchema = () => z.object({
11
15
  links: z.array(z.object({
12
16
  label: z.string(),
@@ -22,21 +26,24 @@ if (locales && Array.isArray(locales)) {
22
26
  collections = {}
23
27
  for (const locale of locales) {
24
28
  const code = (typeof locale === 'string' ? locale : locale.code).replace('-', '_')
29
+ const hasLocaleDocs = docsFolderExists(options.rootDir, code)
25
30
 
26
- collections[`landing_${code}`] = defineCollection({
27
- type: 'page',
28
- source: {
29
- cwd,
30
- include: `${code}/index.md`,
31
- },
32
- })
31
+ if (!hasLandingPage) {
32
+ collections[`landing_${code}`] = defineCollection({
33
+ type: 'page',
34
+ source: {
35
+ cwd,
36
+ include: `${code}/index.md`,
37
+ },
38
+ })
39
+ }
33
40
 
34
41
  collections[`docs_${code}`] = defineCollection({
35
42
  type: 'page',
36
43
  source: {
37
44
  cwd,
38
- include: `${code}/**/*`,
39
- prefix: `/${code}`,
45
+ include: hasLocaleDocs ? `${code}/docs/**` : `${code}/**/*`,
46
+ prefix: hasLocaleDocs ? `/${code}/docs` : `/${code}`,
40
47
  exclude: [`${code}/index.md`],
41
48
  },
42
49
  schema: createDocsSchema(),
@@ -45,22 +52,27 @@ if (locales && Array.isArray(locales)) {
45
52
  }
46
53
  else {
47
54
  collections = {
48
- landing: defineCollection({
55
+ docs: defineCollection({
49
56
  type: 'page',
50
57
  source: {
51
58
  cwd,
52
- include: 'index.md',
59
+ include: hasDocsFolder ? 'docs/**' : '**',
60
+ prefix: hasDocsFolder ? '/docs' : undefined,
61
+ exclude: ['index.md'],
53
62
  },
63
+ schema: createDocsSchema(),
54
64
  }),
55
- docs: defineCollection({
65
+ }
66
+
67
+ // Only define landing collection if user doesn't have their own index.vue
68
+ if (!hasLandingPage) {
69
+ collections.landing = defineCollection({
56
70
  type: 'page',
57
71
  source: {
58
72
  cwd,
59
- include: '**',
60
- exclude: ['index.md'],
73
+ include: 'index.md',
61
74
  },
62
- schema: createDocsSchema(),
63
- }),
75
+ })
64
76
  }
65
77
  }
66
78
 
@@ -18,7 +18,7 @@ const placeholder = computed(() => t('assistant.placeholder'))
18
18
  const shortcutDisplayKeys = computed(() => {
19
19
  const shortcut = focusInputShortcut.value
20
20
  const parts = shortcut.split('_')
21
- return parts.map(part => part === 'meta' ? 'meta' : part.toUpperCase())
21
+ return parts.map((part: string) => part === 'meta' ? 'meta' : part.toUpperCase())
22
22
  })
23
23
 
24
24
  function handleSubmit() {
@@ -62,14 +62,14 @@ defineShortcuts(shortcuts)
62
62
  :animate="{ y: 0, opacity: 1 }"
63
63
  :exit="{ y: 100, opacity: 0 }"
64
64
  :transition="{ duration: 0.2, ease: 'easeOut' }"
65
- class="fixed inset-x-0 z-10 px-4 sm:px-80 bottom-[max(1.5rem,env(safe-area-inset-bottom))]"
65
+ class="pointer-events-none fixed inset-x-0 z-10 bottom-[max(1.5rem,env(safe-area-inset-bottom))] px-4 sm:px-80"
66
66
  style="will-change: transform"
67
67
  >
68
68
  <form
69
- class="flex w-full justify-center"
69
+ class="pointer-events-none flex w-full justify-center"
70
70
  @submit.prevent="handleSubmit"
71
71
  >
72
- <div class="w-full max-w-96">
72
+ <div class="pointer-events-auto w-full max-w-96">
73
73
  <UInput
74
74
  ref="inputRef"
75
75
  v-model="input"
@@ -1,4 +1,5 @@
1
1
  import { defineNuxtModule, extendPages, createResolver } from '@nuxt/kit'
2
+ import { landingPageExists } from '../utils/pages'
2
3
 
3
4
  export default defineNuxtModule({
4
5
  meta: {
@@ -19,23 +20,26 @@ export default defineNuxtModule({
19
20
  })
20
21
  })
21
22
 
22
- extendPages((pages) => {
23
- const landingTemplate = resolve('../app/templates/landing.vue')
23
+ // Only add landing if index.vue is not already defined
24
+ if (!landingPageExists(nuxt.options.rootDir)) {
25
+ extendPages((pages) => {
26
+ const landingTemplate = resolve('../app/templates/landing.vue')
24
27
 
25
- if (isI18nEnabled) {
26
- pages.push({
27
- name: 'lang-index',
28
- path: '/:lang?',
29
- file: landingTemplate,
30
- })
31
- }
32
- else {
33
- pages.push({
34
- name: 'index',
35
- path: '/',
36
- file: landingTemplate,
37
- })
38
- }
39
- })
28
+ if (isI18nEnabled) {
29
+ pages.push({
30
+ name: 'lang-index',
31
+ path: '/:lang?',
32
+ file: landingTemplate,
33
+ })
34
+ }
35
+ else {
36
+ pages.push({
37
+ name: 'index',
38
+ path: '/',
39
+ file: landingTemplate,
40
+ })
41
+ }
42
+ })
43
+ }
40
44
  },
41
45
  })
package/nuxt.config.ts CHANGED
@@ -20,14 +20,16 @@ export default defineNuxtConfig({
20
20
  extendViteConfig((config) => {
21
21
  config.optimizeDeps ||= {}
22
22
  config.optimizeDeps.include ||= []
23
- config.optimizeDeps.include.push(
24
- '@nuxt/content > slugify',
25
- // Fix @vercel/oidc ESM export issue (transitive dep of @ai-sdk/gateway)
26
- '@vercel/oidc',
27
- )
23
+ config.optimizeDeps.include.push('@nuxt/content > slugify')
28
24
  config.optimizeDeps.include = config.optimizeDeps.include
29
25
  .map(id => id.replace(/^@nuxt\/content > /, 'docus > @nuxt/content > '))
30
- .map(id => id.replace(/^@vercel\/oidc$/, 'docus > @vercel/oidc'))
26
+
27
+ // Fix @vercel/oidc ESM export issue (transitive dep of @ai-sdk/gateway)
28
+ // Only needed when AI assistant is enabled.
29
+ if (process.env.AI_GATEWAY_API_KEY) {
30
+ config.optimizeDeps.include.push('@vercel/oidc')
31
+ config.optimizeDeps.include.map(id => id.replace(/^@vercel\/oidc$/, 'docus > @vercel/oidc'))
32
+ }
31
33
  })
32
34
  },
33
35
  ],
@@ -35,6 +37,7 @@ export default defineNuxtConfig({
35
37
  enabled: true,
36
38
  },
37
39
  content: {
40
+ experimental: { sqliteConnector: 'native' },
38
41
  build: {
39
42
  markdown: {
40
43
  highlight: {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "docus",
3
3
  "description": "Nuxt layer for Docus documentation theme",
4
- "version": "5.5.1",
4
+ "version": "5.6.0",
5
5
  "type": "module",
6
6
  "main": "./nuxt.config.ts",
7
7
  "repository": {
@@ -22,22 +22,22 @@
22
22
  "README.md"
23
23
  ],
24
24
  "dependencies": {
25
- "@ai-sdk/gateway": "^3.0.39",
26
- "@ai-sdk/mcp": "^1.0.19",
27
- "@ai-sdk/vue": "3.0.78",
28
- "@iconify-json/lucide": "^1.2.90",
29
- "@iconify-json/simple-icons": "^1.2.70",
30
- "@iconify-json/vscode-icons": "^1.2.41",
25
+ "@ai-sdk/gateway": "^3.0.46",
26
+ "@ai-sdk/mcp": "^1.0.21",
27
+ "@ai-sdk/vue": "3.0.86",
28
+ "@iconify-json/lucide": "^1.2.91",
29
+ "@iconify-json/simple-icons": "^1.2.71",
30
+ "@iconify-json/vscode-icons": "^1.2.43",
31
31
  "@nuxt/content": "^3.11.2",
32
32
  "@nuxt/image": "^2.0.0",
33
33
  "@nuxt/kit": "^4.3.1",
34
34
  "@nuxt/ui": "^4.4.0",
35
35
  "@nuxtjs/i18n": "^10.2.3",
36
- "@nuxtjs/mcp-toolkit": "^0.6.3",
36
+ "@nuxtjs/mcp-toolkit": "^0.7.0",
37
37
  "@nuxtjs/mdc": "^0.20.1",
38
38
  "@nuxtjs/robots": "^5.7.0",
39
39
  "@vueuse/core": "^14.2.1",
40
- "ai": "6.0.78",
40
+ "ai": "6.0.86",
41
41
  "defu": "^6.1.4",
42
42
  "exsolve": "^1.0.8",
43
43
  "git-url-parse": "^16.1.0",
package/utils/pages.ts ADDED
@@ -0,0 +1,22 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { joinURL } from 'ufo'
3
+
4
+ /**
5
+ * Checks if the user has their own index.vue file in the pages folder.
6
+ * When true, the layer should not define the landing collection and / route.
7
+ */
8
+ export function landingPageExists(rootDir: string): boolean {
9
+ const vueLandingPath = joinURL(rootDir, 'app', 'pages', 'index.vue')
10
+ return existsSync(vueLandingPath)
11
+ }
12
+
13
+ /**
14
+ * Checks if a docs folder exists in the content directory.
15
+ * When true, docs should be prefixed with /docs and only include files from the docs folder.
16
+ */
17
+ export function docsFolderExists(rootDir: string, locale?: string): boolean {
18
+ const docsPath = locale
19
+ ? joinURL(rootDir, 'content', locale, 'docs')
20
+ : joinURL(rootDir, 'content', 'docs')
21
+ return existsSync(docsPath)
22
+ }