@vkuttyp/docus 5.4.6

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 (78) hide show
  1. package/README.md +116 -0
  2. package/app/app.config.ts +33 -0
  3. package/app/app.vue +79 -0
  4. package/app/components/IconMenuToggle.vue +83 -0
  5. package/app/components/LanguageSelect.vue +83 -0
  6. package/app/components/OgImage/OgImageDocs.vue +78 -0
  7. package/app/components/OgImage/OgImageLanding.vue +75 -0
  8. package/app/components/app/AppFooter.vue +11 -0
  9. package/app/components/app/AppFooterLeft.vue +5 -0
  10. package/app/components/app/AppFooterRight.vue +30 -0
  11. package/app/components/app/AppHeader.vue +82 -0
  12. package/app/components/app/AppHeaderBody.vue +13 -0
  13. package/app/components/app/AppHeaderCTA.vue +3 -0
  14. package/app/components/app/AppHeaderCenter.vue +10 -0
  15. package/app/components/app/AppHeaderLogo.vue +16 -0
  16. package/app/components/docs/DocsAsideLeftBody.vue +12 -0
  17. package/app/components/docs/DocsAsideLeftTop.vue +3 -0
  18. package/app/components/docs/DocsAsideRightBottom.vue +18 -0
  19. package/app/components/docs/DocsPageHeaderLinks.vue +96 -0
  20. package/app/composables/useDocusI18n.ts +35 -0
  21. package/app/error.vue +79 -0
  22. package/app/layouts/default.vue +5 -0
  23. package/app/layouts/docs.vue +15 -0
  24. package/app/pages/[[lang]]/[...slug].vue +145 -0
  25. package/app/plugins/i18n.ts +40 -0
  26. package/app/templates/landing.vue +44 -0
  27. package/app/types/index.d.ts +42 -0
  28. package/app/utils/navigation.ts +7 -0
  29. package/app/utils/prerender.ts +12 -0
  30. package/content.config.ts +67 -0
  31. package/i18n/locales/ar.json +22 -0
  32. package/i18n/locales/be.json +23 -0
  33. package/i18n/locales/bg.json +22 -0
  34. package/i18n/locales/bn.json +22 -0
  35. package/i18n/locales/ca.json +22 -0
  36. package/i18n/locales/ckb.json +18 -0
  37. package/i18n/locales/cs.json +22 -0
  38. package/i18n/locales/da.json +22 -0
  39. package/i18n/locales/de.json +22 -0
  40. package/i18n/locales/el.json +22 -0
  41. package/i18n/locales/en.json +22 -0
  42. package/i18n/locales/es.json +22 -0
  43. package/i18n/locales/et.json +22 -0
  44. package/i18n/locales/fi.json +22 -0
  45. package/i18n/locales/fr.json +22 -0
  46. package/i18n/locales/he.json +22 -0
  47. package/i18n/locales/hi.json +22 -0
  48. package/i18n/locales/hy.json +22 -0
  49. package/i18n/locales/it.json +22 -0
  50. package/i18n/locales/ja.json +22 -0
  51. package/i18n/locales/kk.json +22 -0
  52. package/i18n/locales/km.json +22 -0
  53. package/i18n/locales/ko.json +22 -0
  54. package/i18n/locales/ky.json +22 -0
  55. package/i18n/locales/lb.json +22 -0
  56. package/i18n/locales/ms.json +22 -0
  57. package/i18n/locales/nb.json +22 -0
  58. package/i18n/locales/nl.json +22 -0
  59. package/i18n/locales/pl.json +23 -0
  60. package/i18n/locales/ro.json +22 -0
  61. package/i18n/locales/ru.json +23 -0
  62. package/i18n/locales/sl.json +22 -0
  63. package/i18n/locales/sv.json +22 -0
  64. package/i18n/locales/uk.json +22 -0
  65. package/i18n/locales/ur.json +22 -0
  66. package/i18n/locales/vi.json +22 -0
  67. package/modules/config.ts +116 -0
  68. package/modules/css.ts +32 -0
  69. package/modules/routing.ts +41 -0
  70. package/nuxt.config.ts +84 -0
  71. package/nuxt.schema.ts +218 -0
  72. package/package.json +55 -0
  73. package/server/mcp/tools/get-page.ts +60 -0
  74. package/server/mcp/tools/list-pages.ts +60 -0
  75. package/server/routes/raw/[...slug].md.get.ts +45 -0
  76. package/server/utils/content.ts +37 -0
  77. package/utils/git.ts +110 -0
  78. package/utils/meta.ts +29 -0
package/README.md ADDED
@@ -0,0 +1,116 @@
1
+ [![docus](https://docus.dev/__og-image__/static/og.png)](https://docus.dev)
2
+
3
+ # Docus
4
+
5
+ > A minimal and beautiful Nuxt layer for documentation websites
6
+
7
+ [![npm version](https://img.shields.io/npm/v/docus.svg)](https://www.npmjs.com/package/docus)
8
+ [![npm downloads](https://img.shields.io/npm/dm/docus.svg)](https://www.npmjs.com/package/docus)
9
+
10
+ This is the official Nuxt layer for [Docus](https://docus.dev), providing a complete documentation theming. It works with the [Docus CLI](https://github.com/nuxt-content/docus/tree/main/cli) for rapid project setup.
11
+
12
+ ## 🚀 Features
13
+
14
+ - ✨ **Beautiful Design** - Clean, modern documentation theme
15
+ - 📱 **Responsive** - Mobile-first responsive design
16
+ - 🌙 **Dark Mode** - Built-in dark/light mode support
17
+ - 🌍 **Internationalization** - Native i18n support with automatic routing and language switching
18
+ - 🔍 **Search** - Full-text search functionality
19
+ - 📝 **Markdown Enhanced** - Extended markdown with custom components
20
+ - 🎨 **Customizable** - Easy theming and customization
21
+ - ⚡ **Fast** - Optimized for performance
22
+ - 🔧 **TypeScript** - Full TypeScript support
23
+ - 🛠️ **CLI Integration** - Works with Docus CLI for quick project setup
24
+
25
+ ## 📦 Installation
26
+
27
+ ```bash
28
+ npm install docus
29
+ ```
30
+
31
+ ## 🏗️ Quick Setup
32
+
33
+ ### Option 1: Docus CLI (Recommended)
34
+
35
+ The easiest way to get started is using the Docus CLI, which automatically sets up a project with this layer:
36
+
37
+ ```bash
38
+ # Create a new documentation project
39
+ npx create-docus my-docs
40
+
41
+ # Navigate to your project
42
+ cd my-docs
43
+
44
+ # Start development
45
+ npm run dev
46
+ ```
47
+
48
+ This creates a complete documentation project pre-configured with `docus`.
49
+
50
+ For multi-language documentation, use the i18n template:
51
+
52
+ ```bash
53
+ # Create a new i18n documentation project
54
+ npx create-docus my-docs -t i18n
55
+ ```
56
+
57
+ ### Option 2: Manual Setup
58
+
59
+ #### Option 2a: Nuxt Config (recommended)
60
+
61
+ Add the layer to your `nuxt.config.ts`:
62
+
63
+ ```typescript
64
+ export default defineNuxtConfig({
65
+ extends: ['docus']
66
+ })
67
+ ```
68
+
69
+ For internationalization, also add the `@nuxtjs/i18n` module:
70
+
71
+ ```typescript
72
+ export default defineNuxtConfig({
73
+ modules: ['@nuxtjs/i18n'],
74
+ i18n: {
75
+ defaultLocale: 'en',
76
+ locales: [
77
+ { code: 'en', name: 'English' },
78
+ { code: 'fr', name: 'Français' },
79
+ ],
80
+ }
81
+ })
82
+ ```
83
+
84
+ #### Option 2b: CLI Usage
85
+
86
+ Use directly with Nuxt CLI:
87
+
88
+ ```bash
89
+ # Development
90
+ nuxt dev --extends docus
91
+
92
+ # Build
93
+ nuxt build --extends docus
94
+ ```
95
+
96
+ ## 🔗 Related Packages
97
+
98
+ - [`create-docus`](https://www.npmjs.com/package/create-docus) - CLI tool to scaffold Docus projects
99
+
100
+ ## 📄 License
101
+
102
+ [MIT License](./LICENSE)
103
+
104
+ ## 🤝 Contributing
105
+
106
+ Contributions are welcome! Please feel free to submit a Pull Request.
107
+
108
+ ## 📞 Support
109
+
110
+ - 📖 [Documentation](https://docus.dev)
111
+ - 🐛 [Issues](https://github.com/nuxt-content/docus/issues)
112
+ - 💬 [Discussions](https://github.com/nuxt-content/docus/discussions)
113
+
114
+ ---
115
+
116
+ Made with ❤️ for the Nuxt community
@@ -0,0 +1,33 @@
1
+ export default defineAppConfig({
2
+ docus: {
3
+ locale: 'en',
4
+ },
5
+ ui: {
6
+ colors: {
7
+ primary: 'emerald',
8
+ neutral: 'zinc',
9
+ },
10
+ commandPalette: {
11
+ slots: {
12
+ item: 'items-center',
13
+ input: '[&_.iconify]:size-4 [&_.iconify]:mx-0.5',
14
+ itemLeadingIcon: 'size-4 mx-0.5',
15
+ },
16
+ },
17
+ contentNavigation: {
18
+ slots: {
19
+ linkLeadingIcon: 'size-4 mr-1',
20
+ linkTrailing: 'hidden',
21
+ },
22
+ defaultVariants: {
23
+ variant: 'link',
24
+ },
25
+ },
26
+ pageLinks: {
27
+ slots: {
28
+ linkLeadingIcon: 'size-4',
29
+ linkLabelExternalIcon: 'size-2.5',
30
+ },
31
+ },
32
+ },
33
+ })
package/app/app.vue ADDED
@@ -0,0 +1,79 @@
1
+ <script setup lang="ts">
2
+ import type { ContentNavigationItem, PageCollections } from '@nuxt/content'
3
+ import * as nuxtUiLocales from '@nuxt/ui/locale'
4
+
5
+ const { seo } = useAppConfig()
6
+ const site = useSiteConfig()
7
+ const { locale, locales, isEnabled, switchLocalePath } = useDocusI18n()
8
+
9
+ const nuxtUiLocale = computed(() => nuxtUiLocales[locale.value as keyof typeof nuxtUiLocales] || nuxtUiLocales.en)
10
+ const lang = computed(() => nuxtUiLocale.value.code)
11
+ const dir = computed(() => nuxtUiLocale.value.dir)
12
+ const collectionName = computed(() => isEnabled.value ? `docs_${locale.value}` : 'docs')
13
+
14
+ useHead({
15
+ meta: [
16
+ { name: 'viewport', content: 'width=device-width, initial-scale=1' },
17
+ ],
18
+ link: [
19
+ { rel: 'icon', href: '/favicon.ico' },
20
+ ],
21
+ htmlAttrs: {
22
+ lang,
23
+ dir,
24
+ },
25
+ })
26
+
27
+ useSeoMeta({
28
+ titleTemplate: seo.titleTemplate,
29
+ title: seo.title,
30
+ description: seo.description,
31
+ ogSiteName: site.name,
32
+ twitterCard: 'summary_large_image',
33
+ })
34
+
35
+ if (isEnabled.value) {
36
+ const route = useRoute()
37
+ const defaultLocale = useRuntimeConfig().public.i18n.defaultLocale!
38
+ onMounted(() => {
39
+ const currentLocale = route.path.split('/')[1]
40
+ if (!locales.some(locale => locale.code === currentLocale)) {
41
+ return navigateTo(switchLocalePath(defaultLocale) as string)
42
+ }
43
+ })
44
+ }
45
+
46
+ const { data: navigation } = await useAsyncData(() => `navigation_${collectionName.value}`, () => queryCollectionNavigation(collectionName.value as keyof PageCollections), {
47
+ transform: (data: ContentNavigationItem[]) => {
48
+ const rootResult = data.find(item => item.path === '/docs')?.children || data || []
49
+
50
+ return rootResult.find(item => item.path === `/${locale.value}`)?.children || rootResult
51
+ },
52
+ watch: [locale],
53
+ })
54
+ const { data: files } = useLazyAsyncData(`search_${collectionName.value}`, () => queryCollectionSearchSections(collectionName.value as keyof PageCollections), {
55
+ server: false,
56
+ watch: [locale],
57
+ })
58
+
59
+ provide('navigation', navigation)
60
+ </script>
61
+
62
+ <template>
63
+ <UApp :locale="nuxtUiLocale">
64
+ <NuxtLoadingIndicator color="var(--ui-primary)" />
65
+
66
+ <AppHeader v-if="$route.meta.header !== false" />
67
+ <NuxtLayout>
68
+ <NuxtPage />
69
+ </NuxtLayout>
70
+ <AppFooter v-if="$route.meta.footer !== false" />
71
+
72
+ <ClientOnly>
73
+ <LazyUContentSearch
74
+ :files="files"
75
+ :navigation="navigation"
76
+ />
77
+ </ClientOnly>
78
+ </UApp>
79
+ </template>
@@ -0,0 +1,83 @@
1
+ <script setup lang="ts">
2
+ import { motion } from 'motion-v'
3
+ import type { VariantType } from 'motion-v'
4
+
5
+ const props = defineProps<{
6
+ open: boolean
7
+ }>()
8
+
9
+ const variants: { [k: string]: VariantType | ((custom: unknown) => VariantType) } = {
10
+ normal: {
11
+ rotate: 0,
12
+ y: 0,
13
+ opacity: 1,
14
+ },
15
+ close: (custom: unknown) => {
16
+ const c = custom as number
17
+ return {
18
+ rotate: c === 1 ? 45 : c === 3 ? -45 : 0,
19
+ y: c === 1 ? 6 : c === 3 ? -6 : 0,
20
+ opacity: c === 2 ? 0 : 1,
21
+ transition: {
22
+ type: 'spring',
23
+ stiffness: 260,
24
+ damping: 20,
25
+ },
26
+ }
27
+ },
28
+ }
29
+
30
+ const state = computed(() => props.open ? 'close' : 'normal')
31
+ </script>
32
+
33
+ <template>
34
+ <UButton
35
+ size="sm"
36
+ variant="ghost"
37
+ color="neutral"
38
+ class="-me-1.5"
39
+ square
40
+ >
41
+ <svg
42
+ xmlns="http://www.w3.org/2000/svg"
43
+ class="size-5"
44
+ viewBox="0 0 24 24"
45
+ fill="none"
46
+ stroke="currentColor"
47
+ stroke-width="2"
48
+ stroke-linecap="round"
49
+ stroke-linejoin="round"
50
+ >
51
+ <motion.line
52
+ x1="4"
53
+ y1="6"
54
+ x2="20"
55
+ y2="6"
56
+ :variants="variants"
57
+ :animate="state"
58
+ :custom="1"
59
+ class="outline-none"
60
+ />
61
+ <motion.line
62
+ x1="4"
63
+ y1="12"
64
+ x2="20"
65
+ y2="12"
66
+ :variants="variants"
67
+ :animate="state"
68
+ :custom="2"
69
+ class="outline-none"
70
+ />
71
+ <motion.line
72
+ x1="4"
73
+ y1="18"
74
+ x2="20"
75
+ y2="18"
76
+ :variants="variants"
77
+ :animate="state"
78
+ :custom="3"
79
+ class="outline-none"
80
+ />
81
+ </svg>
82
+ </UButton>
83
+ </template>
@@ -0,0 +1,83 @@
1
+ <script setup lang="ts">
2
+ const { locale, locales, switchLocalePath } = useDocusI18n()
3
+
4
+ function getEmojiFlag(locale: string): string {
5
+ const languageToCountry: Record<string, string> = {
6
+ ar: 'sa', // Arabic -> Saudi Arabia
7
+ bn: 'bd', // Bengali -> Bangladesh
8
+ ca: 'es', // Catalan -> Spain
9
+ ckb: 'iq', // Central Kurdish -> Iraq
10
+ cs: 'cz', // Czech -> Czech Republic (note: modern country code is actually 'cz')
11
+ da: 'dk', // Danish -> Denmark
12
+ el: 'gr', // Greek -> Greece
13
+ en: 'gb', // English -> Great Britain
14
+ et: 'ee', // Estonian -> Estonia
15
+ he: 'il', // Hebrew -> Israel
16
+ hi: 'in', // Hindi -> India
17
+ hy: 'am', // Armenian -> Armenia
18
+ ja: 'jp', // Japanese -> Japan
19
+ kk: 'kz', // Kazakh -> Kazakhstan
20
+ km: 'kh', // Khmer -> Cambodia
21
+ ko: 'kr', // Korean -> South Korea
22
+ ky: 'kg', // Kyrgyz -> Kyrgyzstan
23
+ lb: 'lu', // Luxembourgish -> Luxembourg
24
+ ms: 'my', // Malay -> Malaysia
25
+ nb: 'no', // Norwegian Bokmål -> Norway
26
+ sl: 'si', // Slovenian -> Slovenia
27
+ sv: 'se', // Swedish -> Sweden
28
+ uk: 'ua', // Ukrainian -> Ukraine
29
+ ur: 'pk', // Urdu -> Pakistan
30
+ vi: 'vn', // Vietnamese -> Vietnam
31
+ es: 'es', // Spanish -> Spain
32
+ }
33
+
34
+ const baseLanguage = locale.split('-')[0]?.toLowerCase() || locale
35
+ const countryCode = languageToCountry[baseLanguage] || locale.replace(/^.*-/, '').slice(0, 2)
36
+
37
+ return countryCode.toUpperCase()
38
+ .split('')
39
+ .map(char => String.fromCodePoint(0x1F1A5 + char.charCodeAt(0)))
40
+ .join('')
41
+ }
42
+ </script>
43
+
44
+ <template>
45
+ <UPopover
46
+ mode="hover"
47
+ :content="{ align: 'end' }"
48
+ >
49
+ <UButton
50
+ color="neutral"
51
+ variant="ghost"
52
+ class="size-8"
53
+ >
54
+ <template #trailing>
55
+ <span class="text-lg">
56
+ {{ getEmojiFlag(locale) }}
57
+ </span>
58
+ </template>
59
+ </UButton>
60
+
61
+ <template #content>
62
+ <ul class="flex flex-col">
63
+ <li
64
+ v-for="localeItem in locales"
65
+ :key="localeItem.code"
66
+ >
67
+ <NuxtLink
68
+ class="flex justify-between py-1.5 px-2 gap-1 hover:bg-muted"
69
+ :to="switchLocalePath(localeItem.code) as string"
70
+ :aria-label="localeItem.name"
71
+ >
72
+ <span class="text-sm">
73
+ {{ localeItem.name }}
74
+ </span>
75
+ <span class="size-5 text-center">
76
+ {{ getEmojiFlag(localeItem.code) }}
77
+ </span>
78
+ </NuxtLink>
79
+ </li>
80
+ </ul>
81
+ </template>
82
+ </UPopover>
83
+ </template>
@@ -0,0 +1,78 @@
1
+ <script lang="ts" setup>
2
+ import { computed } from 'vue'
3
+
4
+ const props = withDefaults(defineProps<{ title?: string, description?: string, headline?: string }>(), {
5
+ title: 'title',
6
+ description: 'description',
7
+ })
8
+
9
+ const title = computed(() => (props.title || '').slice(0, 60))
10
+ const description = computed(() => (props.description || '').slice(0, 200))
11
+ </script>
12
+
13
+ <template>
14
+ <div class="w-full h-full flex flex-col justify-center bg-neutral-900">
15
+ <svg
16
+ class="absolute right-0 top-0 opacity-50"
17
+ width="629"
18
+ height="593"
19
+ viewBox="0 0 629 593"
20
+ fill="none"
21
+ xmlns="http://www.w3.org/2000/svg"
22
+ >
23
+ <g filter="url(#filter0_f_199_94966)">
24
+ <path
25
+ d="M628.5 -578L639.334 -94.4223L806.598 -548.281L659.827 -87.387L965.396 -462.344L676.925 -74.0787L1087.69 -329.501L688.776 -55.9396L1160.22 -164.149L694.095 -34.9354L1175.13 15.7948L692.306 -13.3422L1130.8 190.83L683.602 6.50012L1032.04 341.989L668.927 22.4412L889.557 452.891L649.872 32.7537L718.78 511.519L628.5 36.32L538.22 511.519L607.128 32.7537L367.443 452.891L588.073 22.4412L224.955 341.989L573.398 6.50012L126.198 190.83L564.694 -13.3422L81.8734 15.7948L562.905 -34.9354L96.7839 -164.149L568.224 -55.9396L169.314 -329.501L580.075 -74.0787L291.604 -462.344L597.173 -87.387L450.402 -548.281L617.666 -94.4223L628.5 -578Z"
26
+ fill="white"
27
+ />
28
+ </g>
29
+ <defs>
30
+ <filter
31
+ id="filter0_f_199_94966"
32
+ x="0.873535"
33
+ y="-659"
34
+ width="1255.25"
35
+ height="1251.52"
36
+ filterUnits="userSpaceOnUse"
37
+ color-interpolation-filters="sRGB"
38
+ >
39
+ <feFlood
40
+ flood-opacity="0"
41
+ result="BackgroundImageFix"
42
+ />
43
+ <feBlend
44
+ mode="normal"
45
+ in="SourceGraphic"
46
+ in2="BackgroundImageFix"
47
+ result="shape"
48
+ />
49
+ <feGaussianBlur
50
+ stdDeviation="40.5"
51
+ result="effect1_foregroundBlur_199_94966"
52
+ />
53
+ </filter>
54
+ </defs>
55
+ </svg>
56
+
57
+ <div class="pl-[100px]">
58
+ <p
59
+ v-if="headline"
60
+ class="uppercase text-[24px] text-emerald-500 mb-4 font-semibold"
61
+ >
62
+ {{ headline }}
63
+ </p>
64
+ <h1
65
+ v-if="title"
66
+ class="m-0 text-[75px] font-semibold mb-4 text-white flex items-center"
67
+ >
68
+ <span>{{ title }}</span>
69
+ </h1>
70
+ <p
71
+ v-if="description"
72
+ class="text-[32px] text-neutral-300 leading-tight w-[700px]"
73
+ >
74
+ {{ description }}
75
+ </p>
76
+ </div>
77
+ </div>
78
+ </template>
@@ -0,0 +1,75 @@
1
+ <script lang="ts" setup>
2
+ import { computed } from 'vue'
3
+
4
+ const props = withDefaults(defineProps<{ title?: string, description?: string, headline?: string }>(), {
5
+ title: 'title',
6
+ description: 'description',
7
+ })
8
+
9
+ const title = computed(() => (props.title || '').slice(0, 60))
10
+ const description = computed(() => (props.description || '').slice(0, 200))
11
+ </script>
12
+
13
+ <template>
14
+ <div class="w-full h-full flex items-center justify-center bg-neutral-900">
15
+ <svg
16
+ class="absolute right-0 top-0 opacity-50 "
17
+ width="629"
18
+ height="593"
19
+ viewBox="0 0 629 593"
20
+ fill="none"
21
+ xmlns="http://www.w3.org/2000/svg"
22
+ >
23
+ <g filter="url(#filter0_f_199_94966)">
24
+ <path
25
+ d="M628.5 -578L639.334 -94.4223L806.598 -548.281L659.827 -87.387L965.396 -462.344L676.925 -74.0787L1087.69 -329.501L688.776 -55.9396L1160.22 -164.149L694.095 -34.9354L1175.13 15.7948L692.306 -13.3422L1130.8 190.83L683.602 6.50012L1032.04 341.989L668.927 22.4412L889.557 452.891L649.872 32.7537L718.78 511.519L628.5 36.32L538.22 511.519L607.128 32.7537L367.443 452.891L588.073 22.4412L224.955 341.989L573.398 6.50012L126.198 190.83L564.694 -13.3422L81.8734 15.7948L562.905 -34.9354L96.7839 -164.149L568.224 -55.9396L169.314 -329.501L580.075 -74.0787L291.604 -462.344L597.173 -87.387L450.402 -548.281L617.666 -94.4223L628.5 -578Z"
26
+ fill="white"
27
+ />
28
+ </g>
29
+ <defs>
30
+ <filter
31
+ id="filter0_f_199_94966"
32
+ x="0.873535"
33
+ y="-659"
34
+ width="1255.25"
35
+ height="1251.52"
36
+ filterUnits="userSpaceOnUse"
37
+ color-interpolation-filters="sRGB"
38
+ >
39
+ <feFlood
40
+ flood-opacity="0"
41
+ result="BackgroundImageFix"
42
+ />
43
+ <feBlend
44
+ mode="normal"
45
+ in="SourceGraphic"
46
+ in2="BackgroundImageFix"
47
+ result="shape"
48
+ />
49
+ <feGaussianBlur
50
+ stdDeviation="40.5"
51
+ result="effect1_foregroundBlur_199_94966"
52
+ />
53
+ </filter>
54
+ </defs>
55
+ </svg>
56
+
57
+ <div class="flex flex-col justify-center p-8">
58
+ <div class="flex justify-center mb-8">
59
+ <AppHeaderLogo white />
60
+ </div>
61
+ <h1
62
+ v-if="title"
63
+ class="flex justify-center m-0 text-5xl font-semibold mb-4 text-white"
64
+ >
65
+ <span>{{ title }}</span>
66
+ </h1>
67
+ <p
68
+ v-if="description"
69
+ class="text-center text-2xl text-neutral-300 leading-tight"
70
+ >
71
+ {{ description }}
72
+ </p>
73
+ </div>
74
+ </div>
75
+ </template>
@@ -0,0 +1,11 @@
1
+ <template>
2
+ <UFooter>
3
+ <template #left>
4
+ <AppFooterLeft />
5
+ </template>
6
+
7
+ <template #right>
8
+ <AppFooterRight />
9
+ </template>
10
+ </UFooter>
11
+ </template>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <div class="text-sm text-muted">
3
+ Copyright © {{ new Date().getFullYear() }}
4
+ </div>
5
+ </template>
@@ -0,0 +1,30 @@
1
+ <script setup lang="ts">
2
+ const appConfig = useAppConfig()
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))
18
+ </script>
19
+
20
+ <template>
21
+ <template v-if="links.length">
22
+ <UButton
23
+ v-for="(link, index) of links"
24
+ :key="index"
25
+ size="sm"
26
+ v-bind="{ color: 'neutral', variant: 'ghost', ...link }"
27
+ />
28
+ </template>
29
+ <UColorModeButton />
30
+ </template>
@@ -0,0 +1,82 @@
1
+ <script setup lang="ts">
2
+ import { useDocusI18n } from '../../composables/useDocusI18n'
3
+
4
+ const appConfig = useAppConfig()
5
+ const site = useSiteConfig()
6
+
7
+ const { localePath, isEnabled, locales } = useDocusI18n()
8
+
9
+ const links = computed(() => appConfig.github && appConfig.github.url
10
+ ? [
11
+ {
12
+ 'icon': 'i-simple-icons-github',
13
+ 'to': appConfig.github.url,
14
+ 'target': '_blank',
15
+ 'aria-label': 'GitHub',
16
+ },
17
+ ]
18
+ : [])
19
+ </script>
20
+
21
+ <template>
22
+ <UHeader
23
+ :ui="{ center: 'flex-1' }"
24
+ :to="localePath('/')"
25
+ :title="appConfig.header?.title || site.name"
26
+ >
27
+ <AppHeaderCenter />
28
+
29
+ <template #title>
30
+ <AppHeaderLogo class="h-6 w-auto shrink-0" />
31
+ </template>
32
+
33
+ <template #right>
34
+ <AppHeaderCTA />
35
+
36
+ <template v-if="isEnabled && locales.length > 1">
37
+ <ClientOnly>
38
+ <LanguageSelect />
39
+
40
+ <template #fallback>
41
+ <div class="h-8 w-8 animate-pulse bg-neutral-200 dark:bg-neutral-800 rounded-md" />
42
+ </template>
43
+ </ClientOnly>
44
+
45
+ <USeparator
46
+ orientation="vertical"
47
+ class="h-8"
48
+ />
49
+ </template>
50
+
51
+ <UContentSearchButton class="lg:hidden" />
52
+
53
+ <ClientOnly>
54
+ <UColorModeButton />
55
+
56
+ <template #fallback>
57
+ <div class="h-8 w-8 animate-pulse bg-neutral-200 dark:bg-neutral-800 rounded-md" />
58
+ </template>
59
+ </ClientOnly>
60
+
61
+ <template v-if="links?.length">
62
+ <UButton
63
+ v-for="(link, index) of links"
64
+ :key="index"
65
+ v-bind="{ color: 'neutral', variant: 'ghost', ...link }"
66
+ />
67
+ </template>
68
+ </template>
69
+
70
+ <template #toggle="{ open, toggle }">
71
+ <IconMenuToggle
72
+ :open="open"
73
+ class="lg:hidden"
74
+ @click="toggle"
75
+ />
76
+ </template>
77
+
78
+ <template #body>
79
+ <AppHeaderBody />
80
+ </template>
81
+ </UHeader>
82
+ </template>
@@ -0,0 +1,13 @@
1
+ <script setup lang="ts">
2
+ import type { ContentNavigationItem } from '@nuxt/content'
3
+
4
+ const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
5
+ </script>
6
+
7
+ <template>
8
+ <UContentNavigation
9
+ highlight
10
+ variant="link"
11
+ :navigation="navigation"
12
+ />
13
+ </template>