docus 5.7.0 → 5.8.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.
Files changed (69) hide show
  1. package/app/app.vue +13 -1
  2. package/app/components/LanguageSelect.vue +1 -4
  3. package/app/components/app/AppFooterRight.vue +32 -14
  4. package/app/components/app/AppHeader.vue +10 -0
  5. package/app/components/app/AppHeaderBottom.vue +20 -0
  6. package/app/components/app/AppHeaderBottomRight.vue +3 -0
  7. package/app/components/docs/DocsAsideLeftBody.vue +2 -4
  8. package/app/components/docs/DocsAsideLeftTop.vue +18 -1
  9. package/app/components/docs/DocsAsideMobileBar.vue +88 -0
  10. package/app/components/docs/DocsAsideRight.vue +33 -0
  11. package/app/components/docs/DocsAsideRightBottom.vue +1 -1
  12. package/app/composables/useDocusI18n.ts +21 -5
  13. package/app/composables/useLogoAssets.ts +1 -1
  14. package/app/composables/useSubNavigation.ts +55 -0
  15. package/app/pages/[[lang]]/[...slug].vue +4 -13
  16. package/i18n/locales/ar.json +1 -0
  17. package/i18n/locales/be.json +1 -0
  18. package/i18n/locales/bg.json +1 -0
  19. package/i18n/locales/bn.json +1 -0
  20. package/i18n/locales/ca.json +1 -0
  21. package/i18n/locales/ckb.json +1 -0
  22. package/i18n/locales/cs.json +1 -0
  23. package/i18n/locales/da.json +1 -0
  24. package/i18n/locales/de.json +1 -0
  25. package/i18n/locales/el.json +1 -0
  26. package/i18n/locales/en.json +1 -0
  27. package/i18n/locales/es.json +1 -0
  28. package/i18n/locales/et.json +1 -0
  29. package/i18n/locales/fi.json +1 -0
  30. package/i18n/locales/fr.json +1 -0
  31. package/i18n/locales/he.json +1 -0
  32. package/i18n/locales/hi.json +1 -0
  33. package/i18n/locales/hy.json +1 -0
  34. package/i18n/locales/id.json +1 -0
  35. package/i18n/locales/it.json +1 -0
  36. package/i18n/locales/ja.json +1 -0
  37. package/i18n/locales/kk.json +1 -0
  38. package/i18n/locales/km.json +1 -0
  39. package/i18n/locales/ko.json +1 -0
  40. package/i18n/locales/ky.json +1 -0
  41. package/i18n/locales/lb.json +1 -0
  42. package/i18n/locales/ms.json +1 -0
  43. package/i18n/locales/nb.json +1 -0
  44. package/i18n/locales/nl.json +1 -0
  45. package/i18n/locales/pl.json +1 -0
  46. package/i18n/locales/pt-BR.json +1 -0
  47. package/i18n/locales/ro.json +1 -0
  48. package/i18n/locales/ru.json +1 -0
  49. package/i18n/locales/si.json +1 -0
  50. package/i18n/locales/sl.json +1 -0
  51. package/i18n/locales/sv.json +1 -0
  52. package/i18n/locales/tr.json +1 -0
  53. package/i18n/locales/uk.json +1 -0
  54. package/i18n/locales/ur.json +1 -0
  55. package/i18n/locales/vi.json +1 -0
  56. package/i18n/locales/zh-CN.json +1 -0
  57. package/modules/assistant/runtime/components/AssistantPanel.vue +1 -1
  58. package/modules/assistant/runtime/components/AssistantPreStream.vue +1 -1
  59. package/modules/assistant/runtime/composables/useAssistant.ts +7 -3
  60. package/modules/assistant/runtime/server/api/search.ts +3 -2
  61. package/modules/config.ts +18 -6
  62. package/modules/markdown-rewrite.ts +7 -3
  63. package/modules/routing.ts +4 -1
  64. package/nuxt.config.ts +4 -2
  65. package/nuxt.schema.ts +14 -0
  66. package/package.json +12 -12
  67. package/server/mcp/tools/get-page.ts +2 -4
  68. package/server/mcp/tools/list-pages.ts +2 -1
  69. package/server/routes/sitemap.xml.ts +3 -3
package/app/app.vue CHANGED
@@ -2,6 +2,7 @@
2
2
  import type { ContentNavigationItem, PageCollections } from '@nuxt/content'
3
3
  import * as nuxtUiLocales from '@nuxt/ui/locale'
4
4
  import { transformNavigation } from './utils/navigation'
5
+ import { useSubNavigation } from './composables/useSubNavigation'
5
6
 
6
7
  const { seo } = useAppConfig()
7
8
  const site = useSiteConfig()
@@ -55,6 +56,8 @@ const { data: files } = useLazyAsyncData(`search_${collectionName.value}`, () =>
55
56
  })
56
57
 
57
58
  provide('navigation', navigation)
59
+
60
+ const { subNavigationMode } = useSubNavigation(navigation)
58
61
  </script>
59
62
 
60
63
  <template>
@@ -62,7 +65,7 @@ provide('navigation', navigation)
62
65
  <NuxtLoadingIndicator color="var(--ui-primary)" />
63
66
 
64
67
  <div
65
- class="transition-[margin-right] duration-200 ease-linear will-change-[margin-right]"
68
+ :class="['transition-[margin-right] duration-200 ease-linear will-change-[margin-right]', { 'docus-sub-header': subNavigationMode === 'header' }]"
66
69
  :style="{ marginRight: shouldPushContent ? `${assistantPanelWidth}px` : '0' }"
67
70
  >
68
71
  <AppHeader v-if="$route.meta.header !== false" />
@@ -84,3 +87,12 @@ provide('navigation', navigation)
84
87
  </ClientOnly>
85
88
  </UApp>
86
89
  </template>
90
+
91
+ <style>
92
+ @media (min-width: 1024px) {
93
+ .docus-sub-header {
94
+ /* 64px base header + 48px sub-navigation bar */
95
+ --ui-header-height: 112px;
96
+ }
97
+ }
98
+ </style>
@@ -43,10 +43,7 @@ function getEmojiFlag(locale: string): string {
43
43
  </script>
44
44
 
45
45
  <template>
46
- <UPopover
47
- mode="hover"
48
- :content="{ align: 'end' }"
49
- >
46
+ <UPopover :content="{ align: 'end' }">
50
47
  <UButton
51
48
  color="neutral"
52
49
  variant="ghost"
@@ -1,20 +1,38 @@
1
1
  <script setup lang="ts">
2
2
  const appConfig = useAppConfig()
3
3
 
4
- const links = computed(() => [
5
- ...Object.entries(appConfig.socials || {}).map(([key, url]) => ({
6
- 'icon': `i-simple-icons-${key}`,
7
- 'to': url,
8
- 'target': '_blank',
9
- 'aria-label': `${key} social link`,
10
- })),
11
- appConfig.github && appConfig.github.url && {
12
- 'icon': 'i-simple-icons-github',
13
- 'to': appConfig.github.url,
14
- 'target': '_blank',
15
- 'aria-label': 'GitHub repository',
16
- },
17
- ].filter(Boolean))
4
+ interface FooterLink {
5
+ 'icon': string
6
+ 'to': string
7
+ 'target': '_blank'
8
+ 'aria-label': string
9
+ }
10
+
11
+ const links = computed<FooterLink[]>(() => {
12
+ const socialLinks = Object.entries(appConfig.socials || {}).flatMap(([key, url]) => {
13
+ if (typeof url !== 'string' || !url) {
14
+ return []
15
+ }
16
+
17
+ return [{
18
+ 'icon': `i-simple-icons-${key}`,
19
+ 'to': url,
20
+ 'target': '_blank' as const,
21
+ 'aria-label': `${key} social link`,
22
+ }]
23
+ })
24
+
25
+ const githubLink = appConfig.github && appConfig.github.url
26
+ ? [{
27
+ 'icon': 'i-simple-icons-github',
28
+ 'to': appConfig.github.url,
29
+ 'target': '_blank' as const,
30
+ 'aria-label': 'GitHub repository',
31
+ }]
32
+ : []
33
+
34
+ return [...socialLinks, ...githubLink]
35
+ })
18
36
  </script>
19
37
 
20
38
  <template>
@@ -1,11 +1,13 @@
1
1
  <script setup lang="ts">
2
2
  import { useDocusI18n } from '../../composables/useDocusI18n'
3
+ import { useSubNavigation } from '~/composables/useSubNavigation'
3
4
 
4
5
  const appConfig = useAppConfig()
5
6
  const site = useSiteConfig()
6
7
 
7
8
  const { isEnabled: isAssistantEnabled } = useAssistant()
8
9
  const { localePath, isEnabled, locales } = useDocusI18n()
10
+ const { subNavigationMode } = useSubNavigation()
9
11
 
10
12
  const links = computed(() => appConfig.github && appConfig.github.url
11
13
  ? [
@@ -22,6 +24,7 @@ const links = computed(() => appConfig.github && appConfig.github.url
22
24
  <template>
23
25
  <UHeader
24
26
  :ui="{ center: 'flex-1' }"
27
+ :class="{ 'flex flex-col': subNavigationMode === 'header' }"
25
28
  :to="localePath('/')"
26
29
  :title="appConfig.header?.title || site.name"
27
30
  >
@@ -83,5 +86,12 @@ const links = computed(() => appConfig.github && appConfig.github.url
83
86
  <template #body>
84
87
  <AppHeaderBody />
85
88
  </template>
89
+
90
+ <template
91
+ v-if="subNavigationMode === 'header'"
92
+ #bottom
93
+ >
94
+ <AppHeaderBottom />
95
+ </template>
86
96
  </UHeader>
87
97
  </template>
@@ -0,0 +1,20 @@
1
+ <script setup lang="ts">
2
+ import { useSubNavigation } from '~/composables/useSubNavigation'
3
+
4
+ const { sections } = useSubNavigation()
5
+ </script>
6
+
7
+ <template>
8
+ <USeparator class="hidden lg:flex" />
9
+
10
+ <UContainer class="hidden lg:flex items-center justify-between">
11
+ <UNavigationMenu
12
+ :items="sections"
13
+ variant="pill"
14
+ highlight
15
+ class="-mx-2.5 -mb-px"
16
+ />
17
+
18
+ <AppHeaderBottomRight />
19
+ </UContainer>
20
+ </template>
@@ -0,0 +1,3 @@
1
+ <template>
2
+ <div />
3
+ </template>
@@ -1,12 +1,10 @@
1
1
  <script setup lang="ts">
2
- import type { ContentNavigationItem } from '@nuxt/content'
3
-
4
- const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
2
+ const { sidebarNavigation } = useSubNavigation()
5
3
  </script>
6
4
 
7
5
  <template>
8
6
  <UContentNavigation
9
7
  highlight
10
- :navigation="navigation"
8
+ :navigation="sidebarNavigation"
11
9
  />
12
10
  </template>
@@ -1,3 +1,20 @@
1
+ <script setup lang="ts">
2
+ import { useSubNavigation } from '~/composables/useSubNavigation'
3
+
4
+ const { subNavigationMode, sections } = useSubNavigation()
5
+ </script>
6
+
1
7
  <template>
2
- <div />
8
+ <div
9
+ v-if="subNavigationMode === 'aside'"
10
+ class="mb-2"
11
+ >
12
+ <UPageAnchors :links="sections" />
13
+ <USeparator
14
+ type="dashed"
15
+ class="my-4"
16
+ />
17
+ </div>
18
+
19
+ <div v-else />
3
20
  </template>
@@ -0,0 +1,88 @@
1
+ <script setup lang="ts">
2
+ import { useSubNavigation } from '~/composables/useSubNavigation'
3
+ import type { ContentTocLink } from '@nuxt/ui'
4
+
5
+ defineProps<{
6
+ links?: ContentTocLink[]
7
+ }>()
8
+
9
+ const { subNavigationMode, sidebarNavigation, currentSection } = useSubNavigation()
10
+ const { t } = useDocusI18n()
11
+
12
+ const menuDrawerOpen = ref(false)
13
+ const tocDrawerOpen = ref(false)
14
+ </script>
15
+
16
+ <template>
17
+ <div
18
+ v-if="subNavigationMode"
19
+ class="lg:hidden sticky top-(--ui-header-height) z-10 bg-default/75 backdrop-blur -mx-4 p-2 border-b border-dashed border-default flex justify-between"
20
+ >
21
+ <UDrawer
22
+ v-model:open="menuDrawerOpen"
23
+ direction="left"
24
+ :title="currentSection?.title"
25
+ :handle="false"
26
+ inset
27
+ side="left"
28
+ :ui="{ content: 'w-full max-w-2/3' }"
29
+ >
30
+ <UButton
31
+ :label="t('docs.menu')"
32
+ icon="i-lucide-text-align-start"
33
+ color="neutral"
34
+ variant="link"
35
+ size="xs"
36
+ :aria-label="t('docs.menu')"
37
+ />
38
+
39
+ <template #body>
40
+ <UContentNavigation
41
+ :navigation="sidebarNavigation"
42
+ default-open
43
+ trailing-icon="i-lucide-chevron-right"
44
+ :ui="{ linkTrailingIcon: 'group-data-[state=open]:rotate-90' }"
45
+ highlight
46
+ />
47
+ </template>
48
+ </UDrawer>
49
+
50
+ <UDrawer
51
+ v-model:open="tocDrawerOpen"
52
+ direction="right"
53
+ :handle="false"
54
+ inset
55
+ side="right"
56
+ no-body-styles
57
+ :ui="{ content: 'w-full max-w-2/3' }"
58
+ >
59
+ <UButton
60
+ :label="t('docs.toc')"
61
+ trailing-icon="i-lucide-chevron-right"
62
+ color="neutral"
63
+ variant="link"
64
+ size="xs"
65
+ :aria-label="t('docs.toc')"
66
+ />
67
+
68
+ <template #body>
69
+ <UContentToc
70
+ v-if="links?.length"
71
+ :links="links"
72
+ :open="true"
73
+ default-open
74
+ :ui="{
75
+ root: '!mx-0 !px-1 top-0 overflow-visible',
76
+ container: '!pt-0 border-b-0',
77
+ trailingIcon: 'hidden',
78
+ bottom: 'flex flex-col',
79
+ }"
80
+ >
81
+ <template #bottom>
82
+ <DocsAsideRightBottom />
83
+ </template>
84
+ </UContentToc>
85
+ </template>
86
+ </UDrawer>
87
+ </div>
88
+ </template>
@@ -0,0 +1,33 @@
1
+ <script setup lang="ts">
2
+ import { useSubNavigation } from '~/composables/useSubNavigation'
3
+ import type { DocsCollectionItem } from '@nuxt/content'
4
+
5
+ const props = defineProps<{
6
+ page?: DocsCollectionItem | null
7
+ }>()
8
+
9
+ const links = computed(() => props.page?.body?.toc?.links || [])
10
+
11
+ const { shouldPushContent: shouldHideToc } = useAssistant()
12
+ const { subNavigationMode } = useSubNavigation()
13
+ const appConfig = useAppConfig()
14
+ const { t } = useDocusI18n()
15
+ </script>
16
+
17
+ <template>
18
+ <div>
19
+ <UContentToc
20
+ v-if="links.length && !shouldHideToc"
21
+ highlight
22
+ :title="appConfig.toc?.title || t('docs.toc')"
23
+ :links="links"
24
+ :class="{ 'hidden lg:block': subNavigationMode }"
25
+ >
26
+ <template #bottom>
27
+ <DocsAsideRightBottom />
28
+ </template>
29
+ </UContentToc>
30
+
31
+ <DocsAsideMobileBar :links="links" />
32
+ </div>
33
+ </template>
@@ -16,7 +16,7 @@ const explainIcon = computed(() => appConfig.assistant?.icons?.explain || 'i-luc
16
16
  <template>
17
17
  <div
18
18
  v-if="appConfig.toc?.bottom?.links?.length || showExplainWithAi"
19
- class="hidden lg:block space-y-6"
19
+ class="space-y-6"
20
20
  >
21
21
  <USeparator type="dashed" />
22
22
 
@@ -1,12 +1,27 @@
1
+ import { useNuxtApp, useRuntimeConfig } from '#imports'
1
2
  import type { LocaleObject } from '@nuxtjs/i18n'
3
+ import type { Ref } from 'vue'
4
+ import { ref } from 'vue'
5
+
6
+ type DocusNuxtApp = ReturnType<typeof useNuxtApp> & {
7
+ $i18n?: {
8
+ locale: Ref<string>
9
+ t: (key: string) => string
10
+ }
11
+ $locale?: string
12
+ $localeMessages?: Record<string, unknown>
13
+ $localePath?: (path: string) => string
14
+ $switchLocalePath?: (locale?: string) => string
15
+ }
2
16
 
3
17
  export const useDocusI18n = () => {
4
18
  const config = useRuntimeConfig().public
19
+ const nuxtApp = useNuxtApp() as DocusNuxtApp
5
20
  const isEnabled = ref(!!config.i18n)
6
21
 
7
22
  if (!isEnabled.value) {
8
- const locale = useNuxtApp().$locale as string
9
- const localeMessages = useNuxtApp().$localeMessages as Record<string, unknown>
23
+ const locale = nuxtApp.$locale || 'en'
24
+ const localeMessages = nuxtApp.$localeMessages || {}
10
25
 
11
26
  return {
12
27
  isEnabled,
@@ -21,7 +36,8 @@ export const useDocusI18n = () => {
21
36
  }
22
37
  }
23
38
 
24
- const { locale, t } = useI18n()
39
+ const locale = nuxtApp.$i18n?.locale || ref('en')
40
+ const t = nuxtApp.$i18n?.t || ((key: string) => key)
25
41
  const filteredLocales = (config.docus as { filteredLocales: LocaleObject<string>[] })?.filteredLocales || []
26
42
 
27
43
  return {
@@ -29,7 +45,7 @@ export const useDocusI18n = () => {
29
45
  locale,
30
46
  locales: filteredLocales,
31
47
  t,
32
- localePath: useLocalePath(),
33
- switchLocalePath: useSwitchLocalePath(),
48
+ localePath: nuxtApp.$localePath || ((path: string) => path),
49
+ switchLocalePath: nuxtApp.$switchLocalePath || (() => ''),
34
50
  }
35
51
  }
@@ -65,7 +65,7 @@ function triggerLinkDownload(url: string, filename: string) {
65
65
 
66
66
  export const useLogoAssets = () => {
67
67
  const appConfig = useAppConfig()
68
- const colorMode = useColorMode()
68
+ const colorMode = useColorMode() as { value: string, forced?: boolean }
69
69
  const toast = useToast()
70
70
  const { t } = useDocusI18n()
71
71
 
@@ -0,0 +1,55 @@
1
+ import type { ContentNavigationItem } from '@nuxt/content'
2
+
3
+ function getFirstPagePath(item: ContentNavigationItem): string {
4
+ let current = item
5
+ while (current.children?.length) {
6
+ current = current.children[0]!
7
+ }
8
+ return current.path
9
+ }
10
+
11
+ export function useSubNavigation(providedNavigation?: Ref<ContentNavigationItem[] | null | undefined>) {
12
+ const route = useRoute()
13
+ const appConfig = useAppConfig()
14
+ const navigation = providedNavigation ?? inject<Ref<ContentNavigationItem[]>>('navigation')
15
+
16
+ const isDocsPage = computed(() => route.meta.layout === 'docs')
17
+
18
+ const subNavigationMode = computed(() => {
19
+ if (!isDocsPage.value) return undefined
20
+ return (appConfig.navigation as { sub?: 'header' | 'aside' } | undefined)?.sub
21
+ })
22
+
23
+ const currentSection = computed(() => {
24
+ if (!subNavigationMode.value || !navigation?.value) return undefined
25
+ return navigation.value.find(item =>
26
+ route.path === item.path || route.path.startsWith(item.path + '/'),
27
+ )
28
+ })
29
+
30
+ const sections = computed(() => {
31
+ if (!subNavigationMode.value || !navigation?.value) return []
32
+ return navigation.value
33
+ .filter(item => item.children?.length)
34
+ .map(item => ({
35
+ label: item.title,
36
+ icon: item.icon as string | undefined,
37
+ to: getFirstPagePath(item),
38
+ active: route.path === item.path || route.path.startsWith(item.path + '/'),
39
+ }))
40
+ })
41
+
42
+ const sidebarNavigation = computed(() => {
43
+ if (subNavigationMode.value && currentSection.value) {
44
+ return currentSection.value.children || []
45
+ }
46
+ return navigation?.value || []
47
+ })
48
+
49
+ return {
50
+ subNavigationMode,
51
+ sections,
52
+ currentSection,
53
+ sidebarNavigation,
54
+ }
55
+ }
@@ -131,19 +131,10 @@ addPrerenderPath(`/raw${route.path}.md`)
131
131
  <UContentSurround :surround="surround" />
132
132
  </UPageBody>
133
133
 
134
- <template
135
- v-if="page?.body?.toc?.links?.length && !shouldHideToc"
136
- #right
137
- >
138
- <UContentToc
139
- highlight
140
- :title="appConfig.toc?.title || t('docs.toc')"
141
- :links="page.body?.toc?.links"
142
- >
143
- <template #bottom>
144
- <DocsAsideRightBottom />
145
- </template>
146
- </UContentToc>
134
+ <template #right>
135
+ <DocsAsideRight
136
+ :page="page"
137
+ />
147
138
  </template>
148
139
  </UPage>
149
140
  </template>
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "المجتمع",
18
18
  "toc": "في هذه الصفحة",
19
+ "menu": "قائمة",
19
20
  "report": "الإبلاغ عن مشكلة",
20
21
  "edit": "تحرير هذه الصفحة"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Супольнасць",
18
18
  "toc": "На гэтай старонцы",
19
+ "menu": "Меню",
19
20
  "report": "Паведаміць пра праблему",
20
21
  "edit": "Рэдагаваць гэтую старонку"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Общност",
18
18
  "toc": "На тази страница",
19
+ "menu": "Меню",
19
20
  "report": "Докладване на проблем",
20
21
  "edit": "Редактиране на тази страница"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "কমিউনিটি",
18
18
  "toc": "এই পেজে",
19
+ "menu": "মেনু",
19
20
  "report": "সমস্যা রিপোর্ট করুন",
20
21
  "edit": "এই পেজ সম্পাদনা করুন"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Comunitat",
18
18
  "toc": "En aquesta pàgina",
19
+ "menu": "Menú",
19
20
  "report": "Informar d'un problema",
20
21
  "edit": "Editar aquesta pàgina"
21
22
  },
@@ -12,6 +12,7 @@
12
12
  },
13
13
  "links": "کۆمەڵگا",
14
14
  "toc": "لەم پەڕەدا",
15
+ "menu": "مینیو",
15
16
  "report": "ڕاپۆرتکردنی کێشە",
16
17
  "edit": "دەستکاریکردنی ئەم پەڕەیە"
17
18
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Komunita",
18
18
  "toc": "Na této stránce",
19
+ "menu": "Nabídka",
19
20
  "report": "Nahlásit problém",
20
21
  "edit": "Upravit tuto stránku"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Fællesskab",
18
18
  "toc": "På denne side",
19
+ "menu": "Menu",
19
20
  "report": "Rapporter et problem",
20
21
  "edit": "Rediger denne side"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Community",
18
18
  "toc": "Auf dieser Seite",
19
+ "menu": "Menü",
19
20
  "report": "Problem melden",
20
21
  "edit": "Diese Seite bearbeiten"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Κοινότητα",
18
18
  "toc": "Σε αυτή τη σελίδα",
19
+ "menu": "Μενού",
19
20
  "report": "Αναφορά προβλήματος",
20
21
  "edit": "Επεξεργασία αυτής της σελίδας"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Community",
18
18
  "toc": "On this page",
19
+ "menu": "Menu",
19
20
  "report": "Report an issue",
20
21
  "edit": "Edit this page"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Comunidad",
18
18
  "toc": "En esta página",
19
+ "menu": "Menú",
19
20
  "report": "Reportar un problema",
20
21
  "edit": "Editar esta página"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Kogukond",
18
18
  "toc": "Sellel lehel",
19
+ "menu": "Menüü",
19
20
  "report": "Teata probleemist",
20
21
  "edit": "Muuda seda lehekülge"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Yhteisö",
18
18
  "toc": "Tällä sivulla",
19
+ "menu": "Valikko",
19
20
  "report": "Ilmoita ongelmasta",
20
21
  "edit": "Muokkaa tätä sivua"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Communauté",
18
18
  "toc": "Sur cette page",
19
+ "menu": "Menu",
19
20
  "report": "Signaler un problème",
20
21
  "edit": "Éditer cette page"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "קהילה",
18
18
  "toc": "בעמוד זה",
19
+ "menu": "תפריט",
19
20
  "report": "דווח על בעיה",
20
21
  "edit": "ערוך עמוד זה"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "समुदाय",
18
18
  "toc": "इस पृष्ठ पर",
19
+ "menu": "मेनू",
19
20
  "report": "समस्या की रिपोर्ट करें",
20
21
  "edit": "इस पृष्ठ को संपादित करें"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Համայնք",
18
18
  "toc": "Այս էջում",
19
+ "menu": "Ընտրացանկ",
19
20
  "report": "Հաղորդել խնդրի մասին",
20
21
  "edit": "Խմբագրել այս էջը"
21
22
  },
@@ -19,6 +19,7 @@
19
19
  },
20
20
  "links": "Komunitas",
21
21
  "toc": "Pada halaman ini",
22
+ "menu": "Menu",
22
23
  "report": "Laporkan masalah",
23
24
  "edit": "Ubah halaman ini"
24
25
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Comunità",
18
18
  "toc": "In questa pagina",
19
+ "menu": "Menu",
19
20
  "report": "Segnala un problema",
20
21
  "edit": "Modifica questa pagina"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "コミュニティ",
18
18
  "toc": "このページの内容",
19
+ "menu": "メニュー",
19
20
  "report": "問題を報告",
20
21
  "edit": "このページを編集"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Қауымдастық",
18
18
  "toc": "Осы бетте",
19
+ "menu": "Мәзір",
19
20
  "report": "Мәселе туралы хабарлау",
20
21
  "edit": "Осы бетті өңдеу"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "សហគមន៍",
18
18
  "toc": "នៅលើទំព័រនេះ",
19
+ "menu": "ម៉ឺនុយ",
19
20
  "report": "រាយការណ៍បញ្ហា",
20
21
  "edit": "កែសម្រួលទំព័រនេះ"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "커뮤니티",
18
18
  "toc": "이 페이지에서",
19
+ "menu": "메뉴",
19
20
  "report": "문제 신고",
20
21
  "edit": "이 페이지 편집"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Коом",
18
18
  "toc": "Бул барактта",
19
+ "menu": "Меню",
19
20
  "report": "Көйгөй туурасында кабарлоо",
20
21
  "edit": "Бул баракты түзөтүү"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Gemeinschaft",
18
18
  "toc": "Op dëser Säit",
19
+ "menu": "Menü",
19
20
  "report": "Problem mellen",
20
21
  "edit": "Dës Säit änneren"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Komuniti",
18
18
  "toc": "Dalam halaman ini",
19
+ "menu": "Menu",
19
20
  "report": "Laporkan masalah",
20
21
  "edit": "Edit halaman ini"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Fellesskap",
18
18
  "toc": "På denne siden",
19
+ "menu": "Meny",
19
20
  "report": "Rapporter et problem",
20
21
  "edit": "Rediger denne siden"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Community",
18
18
  "toc": "Op deze pagina",
19
+ "menu": "Menu",
19
20
  "report": "Rapporteer een probleem",
20
21
  "edit": "Bewerk deze pagina"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Społeczność",
18
18
  "toc": "Na tej stronie",
19
+ "menu": "Menu",
19
20
  "report": "Zgłoś problem",
20
21
  "edit": "Edytuj tę stronę"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Comunidade",
18
18
  "toc": "Nesta página",
19
+ "menu": "Menu",
19
20
  "report": "Reportar um erro",
20
21
  "edit": "Editar esta página"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Comunitate",
18
18
  "toc": "Pe această pagină",
19
+ "menu": "Meniu",
19
20
  "report": "Raportează o problemă",
20
21
  "edit": "Editează această pagină"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Сообщество",
18
18
  "toc": "На этой странице",
19
+ "menu": "Меню",
19
20
  "report": "Сообщить о проблеме",
20
21
  "edit": "Редактировать эту страницу"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Community",
18
18
  "toc": "මෙම පිටුවේ",
19
+ "menu": "මෙනුව",
19
20
  "report": "ගැටලුවක් වාර්තා කරන්න",
20
21
  "edit": "මෙම පිටුව සංස්කරණය කරන්න"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Skupnost",
18
18
  "toc": "Na tej strani",
19
+ "menu": "Meni",
19
20
  "report": "Prijavi težavo",
20
21
  "edit": "Uredi to stran"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Gemenskap",
18
18
  "toc": "På denna sida",
19
+ "menu": "Meny",
19
20
  "report": "Rapportera ett problem",
20
21
  "edit": "Redigera denna sida"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Topluluk",
18
18
  "toc": "Bu sayfada",
19
+ "menu": "Menü",
19
20
  "report": "Sorun bildir",
20
21
  "edit": "Bu sayfayı düzenle"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Спільнота",
18
18
  "toc": "На цій сторінці",
19
+ "menu": "Меню",
19
20
  "report": "Повідомити про проблему",
20
21
  "edit": "Редагувати цю сторінку"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "کمیونٹی",
18
18
  "toc": "اس صفحے پر",
19
+ "menu": "مینو",
19
20
  "report": "مسئلہ کی اطلاع دیں",
20
21
  "edit": "اس صفحے کو ترمیم کریں"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "Cộng đồng",
18
18
  "toc": "Trên trang này",
19
+ "menu": "Menu",
19
20
  "report": "Báo cáo sự cố",
20
21
  "edit": "Chỉnh sửa trang này"
21
22
  },
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "links": "社区",
18
18
  "toc": "在此页面上",
19
+ "menu": "菜单",
19
20
  "report": "提交问题报告",
20
21
  "edit": "编辑此页面"
21
22
  },
@@ -25,7 +25,7 @@ const displayPlaceholder = computed(() => t('assistant.placeholder'))
25
25
  const chat = new Chat({
26
26
  messages: messages.value,
27
27
  transport: new DefaultChatTransport({
28
- api: config.public.assistant.apiPath,
28
+ api: (config.app?.baseURL.replace(/\/$/, '') || '') + config.public.assistant.apiPath,
29
29
  }),
30
30
  onError: (error: Error) => {
31
31
  const message = (() => {
@@ -3,7 +3,7 @@ import { ShikiCachedRenderer } from 'shiki-stream/vue'
3
3
  import { useColorMode } from '#imports'
4
4
  import { useHighlighter } from '../composables/useHighlighter'
5
5
 
6
- const colorMode = useColorMode()
6
+ const colorMode = useColorMode() as { value: string, forced?: boolean }
7
7
  const highlighter = await useHighlighter()
8
8
  const props = defineProps<{
9
9
  code: string
@@ -1,5 +1,7 @@
1
1
  import type { UIMessage } from 'ai'
2
+ import { useAppConfig, useRuntimeConfig, useState } from '#imports'
2
3
  import { useMediaQuery } from '@vueuse/core'
4
+ import { computed } from 'vue'
3
5
  import type { FaqCategory, FaqQuestions, LocalizedFaqQuestions } from '../types'
4
6
 
5
7
  function normalizeFaqQuestions(questions: FaqQuestions): FaqCategory[] {
@@ -23,7 +25,10 @@ const PANEL_WIDTH_EXPANDED = 520
23
25
  export function useAssistant() {
24
26
  const config = useRuntimeConfig()
25
27
  const appConfig = useAppConfig()
26
- const isEnabled = computed(() => config.public.assistant?.enabled ?? false)
28
+ const assistantRuntimeConfig = config.public.assistant as { enabled?: boolean } | undefined
29
+ const assistantConfig = appConfig.assistant as { faqQuestions?: FaqQuestions | LocalizedFaqQuestions } | undefined
30
+ const docusRuntimeConfig = appConfig.docus as { locale?: string } | undefined
31
+ const isEnabled = computed(() => assistantRuntimeConfig?.enabled ?? false)
27
32
 
28
33
  const isOpen = useState('assistant-open', () => false)
29
34
  const isExpanded = useState('assistant-expanded', () => false)
@@ -35,14 +40,13 @@ export function useAssistant() {
35
40
  const shouldPushContent = computed(() => !isMobile.value && isOpen.value)
36
41
 
37
42
  const faqQuestions = computed<FaqCategory[]>(() => {
38
- const assistantConfig = appConfig.assistant
39
43
  const faqConfig = assistantConfig?.faqQuestions
40
44
  if (!faqConfig) return []
41
45
 
42
46
  // Check if it's a localized object (has locale keys like 'en', 'fr')
43
47
  if (!Array.isArray(faqConfig)) {
44
48
  const localizedConfig = faqConfig as LocalizedFaqQuestions
45
- const currentLocale = appConfig.docus?.locale || 'en'
49
+ const currentLocale = docusRuntimeConfig?.locale || 'en'
46
50
  const defaultLocale = config.public.i18n?.defaultLocale || 'en'
47
51
 
48
52
  // Try current locale, then default locale, then first available
@@ -60,11 +60,12 @@ export default defineEventHandler(async (event) => {
60
60
 
61
61
  const mcpServer = config.assistant.mcpServer
62
62
  const isExternalUrl = mcpServer.startsWith('http://') || mcpServer.startsWith('https://')
63
+ const baseURL = config.app?.baseURL?.replace(/\/$/, '') || ''
63
64
  const mcpUrl = isExternalUrl
64
65
  ? mcpServer
65
66
  : import.meta.dev
66
- ? `http://localhost:3000${mcpServer}`
67
- : `${getRequestURL(event).origin}${mcpServer}`
67
+ ? `http://localhost:3000${baseURL}${mcpServer}`
68
+ : `${getRequestURL(event).origin}${baseURL}${mcpServer}`
68
69
 
69
70
  const httpClient = await createMCPClient({
70
71
  transport: { type: 'http', url: mcpUrl },
package/modules/config.ts CHANGED
@@ -7,6 +7,13 @@ import { getGitBranch, getGitEnv, getLocalGitInfo } from '../utils/git'
7
7
 
8
8
  const log = logger.withTag('Docus')
9
9
 
10
+ type I18nLocale = string | { code: string, name?: string }
11
+ type DocusI18nOptions = { locales?: I18nLocale[], strategy?: string }
12
+ type RegisterModuleOptions = {
13
+ langDir: string
14
+ locales: Array<{ code: string, name: string, file: string }>
15
+ }
16
+
10
17
  export default defineNuxtModule({
11
18
  meta: {
12
19
  name: 'config',
@@ -54,11 +61,14 @@ export default defineNuxtModule({
54
61
  /*
55
62
  ** I18N
56
63
  */
57
- if (nuxt.options.i18n && nuxt.options.i18n.locales) {
64
+ const typedNuxtOptions = nuxt.options as typeof nuxt.options & { i18n?: DocusI18nOptions }
65
+ const i18nOptions = typedNuxtOptions.i18n
66
+
67
+ if (i18nOptions?.locales) {
58
68
  const { resolve } = createResolver(import.meta.url)
59
69
 
60
70
  // Filter locales to only include existing ones
61
- const filteredLocales = nuxt.options.i18n.locales.filter((locale) => {
71
+ const filteredLocales = i18nOptions.locales.filter((locale: I18nLocale) => {
62
72
  const localeCode = typeof locale === 'string' ? locale : locale.code
63
73
 
64
74
  // Check for JSON locale file
@@ -81,8 +91,8 @@ export default defineNuxtModule({
81
91
  })
82
92
 
83
93
  // Override strategy to prefix
84
- nuxt.options.i18n = {
85
- ...nuxt.options.i18n,
94
+ typedNuxtOptions.i18n = {
95
+ ...i18nOptions,
86
96
  strategy: 'prefix',
87
97
  }
88
98
 
@@ -91,10 +101,12 @@ export default defineNuxtModule({
91
101
  filteredLocales,
92
102
  }
93
103
 
94
- nuxt.hook('i18n:registerModule', (register) => {
104
+ const registerI18nModule = nuxt.hook as unknown as (name: string, callback: (register: (options: RegisterModuleOptions) => void) => void) => void
105
+
106
+ registerI18nModule('i18n:registerModule', (register) => {
95
107
  const langDir = resolve('../i18n/locales')
96
108
 
97
- const locales = filteredLocales?.map((locale) => {
109
+ const locales = filteredLocales.map((locale: I18nLocale) => {
98
110
  return typeof locale === 'string'
99
111
  ? {
100
112
  code: locale,
@@ -4,6 +4,9 @@ import { readFile, writeFile } from 'node:fs/promises'
4
4
 
5
5
  const log = logger.withTag('Docus')
6
6
 
7
+ type I18nLocale = string | { code: string }
8
+ type DocusI18nOptions = { locales?: I18nLocale[] }
9
+
7
10
  export default defineNuxtModule({
8
11
  meta: {
9
12
  name: 'markdown-rewrite',
@@ -44,13 +47,14 @@ export default defineNuxtModule({
44
47
  ]
45
48
 
46
49
  // Check if i18n is enabled
47
- const isI18nEnabled = !!(nuxt.options.i18n && nuxt.options.i18n.locales)
50
+ const i18nOptions = (nuxt.options as typeof nuxt.options & { i18n?: DocusI18nOptions }).i18n
51
+ const isI18nEnabled = !!i18nOptions?.locales
48
52
  let localeCodes: string[] = []
49
53
 
50
54
  if (isI18nEnabled) {
51
55
  // Get locale codes
52
- const locales = nuxt.options.i18n.locales || []
53
- localeCodes = locales.map((locale) => {
56
+ const locales = i18nOptions?.locales || []
57
+ localeCodes = locales.map((locale: I18nLocale) => {
54
58
  return typeof locale === 'string' ? locale : locale.code
55
59
  })
56
60
 
@@ -1,6 +1,8 @@
1
1
  import { defineNuxtModule, extendPages, createResolver } from '@nuxt/kit'
2
2
  import { landingPageExists } from '../utils/pages'
3
3
 
4
+ type DocusI18nOptions = { locales?: Array<string | { code: string }> }
5
+
4
6
  export default defineNuxtModule({
5
7
  meta: {
6
8
  name: 'routing',
@@ -8,7 +10,8 @@ export default defineNuxtModule({
8
10
  async setup(_options, nuxt) {
9
11
  const { resolve } = createResolver(import.meta.url)
10
12
 
11
- const isI18nEnabled = !!(nuxt.options.i18n && nuxt.options.i18n.locales)
13
+ const i18nOptions = (nuxt.options as typeof nuxt.options & { i18n?: DocusI18nOptions }).i18n
14
+ const isI18nEnabled = !!i18nOptions?.locales
12
15
 
13
16
  // Ensure useDocusI18n is available in the app
14
17
  nuxt.hook('imports:extend', (imports) => {
package/nuxt.config.ts CHANGED
@@ -3,6 +3,8 @@ import { extendViteConfig, createResolver, useNuxt } from '@nuxt/kit'
3
3
 
4
4
  const { resolve } = createResolver(import.meta.url)
5
5
 
6
+ type DocusI18nOptions = { locales?: Array<string | { code: string }> }
7
+
6
8
  export default defineNuxtConfig({
7
9
  modules: [
8
10
  resolve('./modules/config'),
@@ -87,14 +89,14 @@ export default defineNuxtConfig({
87
89
  'nitro:config'(nitroConfig) {
88
90
  const nuxt = useNuxt()
89
91
 
90
- const i18nOptions = nuxt.options.i18n
92
+ const i18nOptions = (nuxt.options as typeof nuxt.options & { i18n?: DocusI18nOptions }).i18n
91
93
 
92
94
  const routes: string[] = []
93
95
  if (!i18nOptions) {
94
96
  routes.push('/')
95
97
  }
96
98
  else {
97
- routes.push(...(i18nOptions.locales?.map(locale => typeof locale === 'string' ? `/${locale}` : `/${locale.code}`) || []))
99
+ routes.push(...(i18nOptions.locales?.map((locale: string | { code: string }) => typeof locale === 'string' ? `/${locale}` : `/${locale.code}`) || []))
98
100
  }
99
101
 
100
102
  nitroConfig.prerender = nitroConfig.prerender || {}
package/nuxt.schema.ts CHANGED
@@ -193,6 +193,20 @@ export default defineNuxtSchema({
193
193
  }),
194
194
  },
195
195
  }),
196
+ navigation: group({
197
+ title: 'Navigation',
198
+ description: 'Navigation configuration.',
199
+ icon: 'i-lucide-navigation',
200
+ fields: {
201
+ sub: field({
202
+ type: 'string',
203
+ title: 'Sub Navigation',
204
+ description: 'Enable sub-navigation for multi-section docs. Use "header" for a secondary tab bar below the header, or "aside" for section anchors at the top of the sidebar.',
205
+ icon: 'i-lucide-layout-panel-left',
206
+ default: '',
207
+ }),
208
+ },
209
+ }),
196
210
  socials: field({
197
211
  type: 'object',
198
212
  title: 'Social Networks',
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.7.0",
4
+ "version": "5.8.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.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",
25
+ "@ai-sdk/gateway": "^3.0.66",
26
+ "@ai-sdk/mcp": "^1.0.25",
27
+ "@ai-sdk/vue": "3.0.116",
28
+ "@iconify-json/lucide": "^1.2.96",
29
+ "@iconify-json/simple-icons": "^1.2.73",
30
+ "@iconify-json/vscode-icons": "^1.2.45",
31
31
  "@nuxt/content": "^3.12.0",
32
32
  "@nuxt/image": "^2.0.0",
33
33
  "@nuxt/kit": "^4.3.1",
34
- "@nuxt/ui": "^4.5.0",
34
+ "@nuxt/ui": "^4.5.1",
35
35
  "@nuxtjs/i18n": "^10.2.3",
36
36
  "@nuxtjs/mcp-toolkit": "^0.7.0",
37
- "@nuxtjs/mdc": "^0.20.1",
38
- "@nuxtjs/robots": "^5.7.0",
37
+ "@nuxtjs/mdc": "^0.20.2",
38
+ "@nuxtjs/robots": "^5.7.1",
39
39
  "@vueuse/core": "^14.2.1",
40
- "ai": "6.0.86",
40
+ "ai": "6.0.116",
41
41
  "defu": "^6.1.4",
42
42
  "exsolve": "^1.0.8",
43
43
  "git-url-parse": "^16.1.0",
@@ -51,7 +51,7 @@
51
51
  "@shikijs/langs": "^3.22.0",
52
52
  "@shikijs/themes": "^3.22.0",
53
53
  "shiki-stream": "^0.1.4",
54
- "tailwindcss": "^4.1.18",
54
+ "tailwindcss": "^4.2.1",
55
55
  "ufo": "^1.6.3",
56
56
  "zod": "^4.3.6",
57
57
  "zod-to-json-schema": "^3.25.1"
@@ -24,7 +24,7 @@ WORKFLOW: This tool returns the complete page content including title, descripti
24
24
  handler: async ({ path }) => {
25
25
  const event = useEvent()
26
26
  const config = useRuntimeConfig(event).public
27
- const siteUrl = import.meta.dev ? 'http://localhost:3000' : inferSiteURL()
27
+ const siteUrl = getRequestURL(event).origin || inferSiteURL()
28
28
 
29
29
  const availableLocales = getAvailableLocales(config)
30
30
  const collectionName = config.i18n?.locales
@@ -41,9 +41,7 @@ WORKFLOW: This tool returns the complete page content including title, descripti
41
41
  return errorResult('Page not found')
42
42
  }
43
43
 
44
- const content = await $fetch<string>(`/raw${path}.md`, {
45
- baseURL: siteUrl,
46
- })
44
+ const content = await event.$fetch<string>(`/raw${path}.md`)
47
45
 
48
46
  return jsonResult({
49
47
  title: page.title,
@@ -2,6 +2,7 @@ import { z } from 'zod'
2
2
  import { queryCollection } from '@nuxt/content/server'
3
3
  import type { Collections } from '@nuxt/content'
4
4
  import { getCollectionsToQuery, getAvailableLocales } from '../../utils/content'
5
+ import { inferSiteURL } from '../../../utils/meta'
5
6
 
6
7
  export default defineMcpTool({
7
8
  description: `Lists all available documentation pages with their categories and basic information.
@@ -30,7 +31,7 @@ OUTPUT: Returns a structured list with:
30
31
  const event = useEvent()
31
32
  const config = useRuntimeConfig(event).public
32
33
 
33
- const siteUrl = import.meta.dev ? 'http://localhost:3000' : getRequestURL(event).origin
34
+ const siteUrl = getRequestURL(event).origin || inferSiteURL()
34
35
  const availableLocales = getAvailableLocales(config)
35
36
  const collections = getCollectionsToQuery(locale, availableLocales)
36
37
 
@@ -27,11 +27,11 @@ export default defineEventHandler(async (event) => {
27
27
 
28
28
  for (const collection of collections) {
29
29
  try {
30
- const pages = await queryCollection(event, collection as 'docs').all()
30
+ const pages = await (queryCollection as unknown as (event: unknown, collection: string) => { all: () => Promise<Array<Record<string, unknown> & { path?: string }>> })(event, collection).all()
31
31
 
32
32
  for (const page of pages) {
33
- const meta = page as unknown as Record<string, unknown>
34
- const pagePath = (page.path as string) || '/'
33
+ const meta = page as Record<string, unknown>
34
+ const pagePath = page.path || '/'
35
35
 
36
36
  // Skip pages with sitemap: false in frontmatter
37
37
  if (meta.sitemap === false) continue