docus 5.6.1 → 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 (74) hide show
  1. package/app/app.vue +13 -1
  2. package/app/assets/icons/LICENSE +14 -0
  3. package/app/assets/icons/ai.svg +1 -0
  4. package/app/components/LanguageSelect.vue +1 -4
  5. package/app/components/OgImage/OgImageDocs.vue +2 -4
  6. package/app/components/OgImage/OgImageLanding.vue +36 -13
  7. package/app/components/app/AppFooterRight.vue +32 -14
  8. package/app/components/app/AppHeader.vue +10 -0
  9. package/app/components/app/AppHeaderBottom.vue +20 -0
  10. package/app/components/app/AppHeaderBottomRight.vue +3 -0
  11. package/app/components/docs/DocsAsideLeftBody.vue +2 -4
  12. package/app/components/docs/DocsAsideLeftTop.vue +18 -1
  13. package/app/components/docs/DocsAsideMobileBar.vue +88 -0
  14. package/app/components/docs/DocsAsideRight.vue +33 -0
  15. package/app/components/docs/DocsAsideRightBottom.vue +1 -1
  16. package/app/composables/useDocusI18n.ts +21 -5
  17. package/app/composables/useLogoAssets.ts +1 -1
  18. package/app/composables/useSubNavigation.ts +55 -0
  19. package/app/pages/[[lang]]/[...slug].vue +4 -13
  20. package/i18n/locales/ar.json +1 -0
  21. package/i18n/locales/be.json +1 -0
  22. package/i18n/locales/bg.json +1 -0
  23. package/i18n/locales/bn.json +1 -0
  24. package/i18n/locales/ca.json +1 -0
  25. package/i18n/locales/ckb.json +1 -0
  26. package/i18n/locales/cs.json +1 -0
  27. package/i18n/locales/da.json +1 -0
  28. package/i18n/locales/de.json +1 -0
  29. package/i18n/locales/el.json +1 -0
  30. package/i18n/locales/en.json +1 -0
  31. package/i18n/locales/es.json +1 -0
  32. package/i18n/locales/et.json +1 -0
  33. package/i18n/locales/fi.json +1 -0
  34. package/i18n/locales/fr.json +1 -0
  35. package/i18n/locales/he.json +1 -0
  36. package/i18n/locales/hi.json +1 -0
  37. package/i18n/locales/hy.json +1 -0
  38. package/i18n/locales/id.json +1 -0
  39. package/i18n/locales/it.json +1 -0
  40. package/i18n/locales/ja.json +1 -0
  41. package/i18n/locales/kk.json +1 -0
  42. package/i18n/locales/km.json +1 -0
  43. package/i18n/locales/ko.json +1 -0
  44. package/i18n/locales/ky.json +1 -0
  45. package/i18n/locales/lb.json +1 -0
  46. package/i18n/locales/ms.json +1 -0
  47. package/i18n/locales/nb.json +1 -0
  48. package/i18n/locales/nl.json +1 -0
  49. package/i18n/locales/pl.json +1 -0
  50. package/i18n/locales/pt-BR.json +1 -0
  51. package/i18n/locales/ro.json +1 -0
  52. package/i18n/locales/ru.json +1 -0
  53. package/i18n/locales/si.json +1 -0
  54. package/i18n/locales/sl.json +1 -0
  55. package/i18n/locales/sv.json +1 -0
  56. package/i18n/locales/tr.json +1 -0
  57. package/i18n/locales/uk.json +1 -0
  58. package/i18n/locales/ur.json +1 -0
  59. package/i18n/locales/vi.json +1 -0
  60. package/i18n/locales/zh-CN.json +1 -0
  61. package/modules/assistant/runtime/components/AssistantChat.vue +2 -2
  62. package/modules/assistant/runtime/components/AssistantPanel.vue +1 -1
  63. package/modules/assistant/runtime/components/AssistantPreStream.vue +1 -1
  64. package/modules/assistant/runtime/composables/useAssistant.ts +7 -3
  65. package/modules/assistant/runtime/server/api/search.ts +3 -2
  66. package/modules/config.ts +18 -6
  67. package/modules/markdown-rewrite.ts +7 -3
  68. package/modules/routing.ts +4 -1
  69. package/nuxt.config.ts +24 -2
  70. package/nuxt.schema.ts +15 -1
  71. package/package.json +13 -14
  72. package/server/mcp/tools/get-page.ts +2 -4
  73. package/server/mcp/tools/list-pages.ts +2 -1
  74. 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>
@@ -0,0 +1,14 @@
1
+ Copyright © Nucleo
2
+
3
+ Version 1.3, January 3, 2024
4
+
5
+ Nucleo Icons
6
+
7
+ https://nucleoapp.com/
8
+
9
+ - Redistribution of icons is prohibited.
10
+ - Icons are restricted for use only within the product they are bundled with.
11
+
12
+ For more details:
13
+
14
+ https://nucleoapp.com/license
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" id="artificial-intelligence" aria-hidden="true" viewBox="0 0 20 20"><title>artificial-intelligence</title><g fill="currentColor"><polyline fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" points="10.5 15 7 5 6 5 2.5 15"/><line x1="9.586" x2="3.414" y1="12.5" y2="12.5" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><polyline fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" points="13.5 9 15.5 9 15.5 15"/><line x1="13.5" x2="17.5" y1="15" y2="15" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path fill="currentColor" stroke-width="0" d="m17.6527,3.9647l-1.2005-.4503-.4506-1.2005c-.1562-.4185-.8468-.4185-1.0031,0l-.4506,1.2005-1.2005.4503c-.2086.0785-.3474.2783-.3474.5015s.1388.4231.3474.5015l1.2005.4503.4506,1.2005c.0781.2093.2783.3477.5015.3477s.4234-.1385.5015-.3477l.4506-1.2005,1.2005-.4503c.2086-.0785.3474-.2783.3474-.5015s-.1388-.4231-.3474-.5015h0Z"/></g></svg>
@@ -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,13 +1,11 @@
1
1
  <script lang="ts" setup>
2
- import { computed } from 'vue'
3
-
4
2
  const props = withDefaults(defineProps<{ title?: string, description?: string, headline?: string }>(), {
5
3
  title: 'title',
6
4
  description: 'description',
7
5
  })
8
6
 
9
- const title = computed(() => (props.title || '').slice(0, 60))
10
- const description = computed(() => (props.description || '').slice(0, 200))
7
+ const title = (props.title || '').slice(0, 60)
8
+ const description = (props.description || '').slice(0, 200)
11
9
  </script>
12
10
 
13
11
  <template>
@@ -1,19 +1,37 @@
1
1
  <script lang="ts" setup>
2
- import { computed } from 'vue'
3
-
4
- const props = withDefaults(defineProps<{ title?: string, description?: string, headline?: string }>(), {
2
+ const props = withDefaults(defineProps<{ title?: string, description?: string }>(), {
5
3
  title: 'title',
6
4
  description: 'description',
7
5
  })
8
6
 
9
- const title = computed(() => (props.title || '').slice(0, 60))
10
- const description = computed(() => (props.description || '').slice(0, 200))
7
+ const appConfig = useAppConfig()
8
+ const logoPath = appConfig.header?.logo?.dark || appConfig.header?.logo?.light
9
+
10
+ const logoSvg = await fetchLogoSvg(logoPath)
11
+
12
+ const title = (props.title || '').slice(0, 60)
13
+ const description = (props.description || '').slice(0, 200)
14
+
15
+ async function fetchLogoSvg(path?: string): Promise<string> {
16
+ if (!path) return ''
17
+ try {
18
+ // eslint-disable-next-line
19
+ // @ts-ignore
20
+ const { url: siteUrl } = useSiteConfig()
21
+ const url = path.startsWith('http') ? path : `${siteUrl}${path}`
22
+ const svg = await $fetch<string>(url, { responseType: 'text' })
23
+ return svg.replace('<svg', '<svg width="100%" height="100%"')
24
+ }
25
+ catch {
26
+ return ''
27
+ }
28
+ }
11
29
  </script>
12
30
 
13
31
  <template>
14
32
  <div class="w-full h-full flex items-center justify-center bg-neutral-900">
15
33
  <svg
16
- class="absolute right-0 top-0 opacity-50 "
34
+ class="absolute right-0 top-0 opacity-50"
17
35
  width="629"
18
36
  height="593"
19
37
  viewBox="0 0 629 593"
@@ -54,19 +72,24 @@ const description = computed(() => (props.description || '').slice(0, 200))
54
72
  </defs>
55
73
  </svg>
56
74
 
57
- <div class="flex flex-col justify-center p-8">
58
- <div class="flex justify-center mb-8">
59
- <AppHeaderLogo white />
60
- </div>
75
+ <div
76
+ class="flex flex-col items-center justify-center p-8"
77
+ >
78
+ <div
79
+ v-if="logoSvg"
80
+ class="flex items-center justify-center mb-10 max-w-[900px]"
81
+ style="width: 72px; height: 72px;"
82
+ v-html="logoSvg"
83
+ />
61
84
  <h1
62
85
  v-if="title"
63
- class="flex justify-center m-0 text-5xl font-semibold mb-4 text-white"
86
+ class="m-0 text-5xl font-semibold mb-4 text-white text-center"
64
87
  >
65
- <span>{{ title }}</span>
88
+ {{ title }}
66
89
  </h1>
67
90
  <p
68
91
  v-if="description"
69
- class="text-center text-2xl text-neutral-300 leading-tight"
92
+ class="text-center text-2xl text-neutral-300 leading-tight max-w-[800px]"
70
93
  >
71
94
  {{ description }}
72
95
  </p>
@@ -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
  },