docus 1.0.7 → 2.0.0-alpha.1
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/LICENSE +21 -0
- package/README.md +23 -16
- package/app/app/app.config.ts +33 -0
- package/app/app/app.vue +54 -0
- package/app/app/assets/css/main.css +5 -0
- package/app/app/components/IconMenuToggle.vue +81 -0
- package/app/app/components/OgImage/OgImageDocs.vue +76 -0
- package/app/app/components/OgImage/OgImageLanding.vue +73 -0
- package/app/app/components/app/AppFooter.vue +40 -0
- package/app/app/components/app/AppHeader.vue +57 -0
- package/app/app/components/app/AppHeaderBody.vue +13 -0
- package/app/app/components/app/AppHeaderCTA.vue +3 -0
- package/app/app/components/app/AppHeaderCenter.vue +6 -0
- package/app/app/components/app/AppHeaderLogo.vue +16 -0
- package/app/app/components/docs/DocsAsideLeftTop.vue +3 -0
- package/app/app/components/docs/DocsAsideRightBottom.vue +17 -0
- package/app/app/components/docs/DocsPageHeaderLinks.vue +91 -0
- package/app/app/error.vue +42 -0
- package/app/app/layouts/docs.vue +25 -0
- package/app/app/pages/[...slug].vue +112 -0
- package/app/app/pages/index.vue +37 -0
- package/app/content.config.ts +31 -0
- package/app/nuxt.config.ts +29 -0
- package/app/nuxt.schema.ts +255 -0
- package/app/server/routes/raw/[...slug].md.get.ts +24 -0
- package/dist/main.mjs +238 -0
- package/package.json +59 -58
- package/dist/create-docus/create-docus.js +0 -7
- package/dist/create-docus/helpers.js +0 -244
- package/dist/create-docus/index.js +0 -87
- package/dist/index.js +0 -10
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { NuxtError } from '#app'
|
|
3
|
+
|
|
4
|
+
defineProps<{
|
|
5
|
+
error: NuxtError
|
|
6
|
+
}>()
|
|
7
|
+
|
|
8
|
+
useHead({
|
|
9
|
+
htmlAttrs: {
|
|
10
|
+
lang: 'en',
|
|
11
|
+
},
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
useSeoMeta({
|
|
15
|
+
title: 'Page not found',
|
|
16
|
+
description: 'We are sorry but this page could not be found.',
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const { data: navigation } = await useAsyncData('navigation', () => queryCollectionNavigation('docs'))
|
|
20
|
+
const { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSections('docs'), {
|
|
21
|
+
server: false,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
provide('navigation', navigation)
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<template>
|
|
28
|
+
<UApp>
|
|
29
|
+
<AppHeader />
|
|
30
|
+
|
|
31
|
+
<UError :error="error" />
|
|
32
|
+
|
|
33
|
+
<AppFooter />
|
|
34
|
+
|
|
35
|
+
<ClientOnly>
|
|
36
|
+
<LazyUContentSearch
|
|
37
|
+
:files="files"
|
|
38
|
+
:navigation="navigation"
|
|
39
|
+
/>
|
|
40
|
+
</ClientOnly>
|
|
41
|
+
</UApp>
|
|
42
|
+
</template>
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
<UContainer>
|
|
9
|
+
<UPage>
|
|
10
|
+
<template #left>
|
|
11
|
+
<UPageAside>
|
|
12
|
+
<DocsAsideLeftTop />
|
|
13
|
+
|
|
14
|
+
<UContentNavigation
|
|
15
|
+
highlight
|
|
16
|
+
variant="link"
|
|
17
|
+
:navigation="navigation"
|
|
18
|
+
/>
|
|
19
|
+
</UPageAside>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<slot />
|
|
23
|
+
</UPage>
|
|
24
|
+
</UContainer>
|
|
25
|
+
</template>
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { ContentNavigationItem } from '@nuxt/content'
|
|
3
|
+
import { findPageHeadline } from '#ui-pro/utils/content'
|
|
4
|
+
|
|
5
|
+
definePageMeta({
|
|
6
|
+
layout: 'docs',
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
const route = useRoute()
|
|
10
|
+
const appConfig = useAppConfig()
|
|
11
|
+
const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
|
|
12
|
+
|
|
13
|
+
const { data: page } = await useAsyncData(route.path, () => queryCollection('docs').path(route.path).first())
|
|
14
|
+
if (!page.value) {
|
|
15
|
+
throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const { data: surround } = await useAsyncData(`${route.path}-surround`, () => {
|
|
19
|
+
return queryCollectionItemSurroundings('docs', route.path, {
|
|
20
|
+
fields: ['description'],
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const title = page.value.seo?.title || page.value.title
|
|
25
|
+
const description = page.value.seo?.description || page.value.description
|
|
26
|
+
|
|
27
|
+
useSeoMeta({
|
|
28
|
+
title,
|
|
29
|
+
ogTitle: title,
|
|
30
|
+
description,
|
|
31
|
+
ogDescription: description,
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const headline = computed(() => findPageHeadline(navigation?.value, page.value))
|
|
35
|
+
|
|
36
|
+
defineOgImageComponent('Docs', {
|
|
37
|
+
headline: headline.value,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const editLink = computed(() => {
|
|
41
|
+
return appConfig.github && `${appConfig.github.url}/edit/${appConfig.github.branch}/content/${page.value?.stem}.${page.value?.extension}`
|
|
42
|
+
})
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<template>
|
|
46
|
+
<UPage v-if="page">
|
|
47
|
+
<UPageHeader
|
|
48
|
+
:title="page.title"
|
|
49
|
+
:description="page.description"
|
|
50
|
+
:links="page.links"
|
|
51
|
+
:headline="headline"
|
|
52
|
+
:ui="{
|
|
53
|
+
wrapper: 'flex-row items-center flex-wrap justify-between',
|
|
54
|
+
}"
|
|
55
|
+
>
|
|
56
|
+
<template #links>
|
|
57
|
+
<DocsPageHeaderLinks />
|
|
58
|
+
</template>
|
|
59
|
+
</UPageHeader>
|
|
60
|
+
|
|
61
|
+
<UPageBody>
|
|
62
|
+
<ContentRenderer
|
|
63
|
+
v-if="page"
|
|
64
|
+
:value="page"
|
|
65
|
+
/>
|
|
66
|
+
|
|
67
|
+
<USeparator>
|
|
68
|
+
<div
|
|
69
|
+
v-if="editLink"
|
|
70
|
+
class="flex items-center gap-2 text-sm text-muted"
|
|
71
|
+
>
|
|
72
|
+
<UButton
|
|
73
|
+
variant="link"
|
|
74
|
+
color="neutral"
|
|
75
|
+
:to="editLink"
|
|
76
|
+
target="_blank"
|
|
77
|
+
icon="i-lucide-pen"
|
|
78
|
+
:ui="{ leadingIcon: 'size-4' }"
|
|
79
|
+
>
|
|
80
|
+
Edit this page
|
|
81
|
+
</UButton>
|
|
82
|
+
or
|
|
83
|
+
<UButton
|
|
84
|
+
variant="link"
|
|
85
|
+
color="neutral"
|
|
86
|
+
:to="`${appConfig.github.url}/issues/new/choose`"
|
|
87
|
+
target="_blank"
|
|
88
|
+
icon="i-lucide-alert-circle"
|
|
89
|
+
:ui="{ leadingIcon: 'size-4' }"
|
|
90
|
+
>
|
|
91
|
+
Report an issue
|
|
92
|
+
</UButton>
|
|
93
|
+
</div>
|
|
94
|
+
</USeparator>
|
|
95
|
+
<UContentSurround :surround="surround" />
|
|
96
|
+
</UPageBody>
|
|
97
|
+
|
|
98
|
+
<template
|
|
99
|
+
v-if="page?.body?.toc?.links?.length"
|
|
100
|
+
#right
|
|
101
|
+
>
|
|
102
|
+
<UContentToc
|
|
103
|
+
:title="appConfig.toc?.title || 'Table of Contents'"
|
|
104
|
+
:links="page.body?.toc?.links"
|
|
105
|
+
>
|
|
106
|
+
<template #bottom>
|
|
107
|
+
<DocsAsideRightBottom />
|
|
108
|
+
</template>
|
|
109
|
+
</UContentToc>
|
|
110
|
+
</template>
|
|
111
|
+
</UPage>
|
|
112
|
+
</template>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const { data: page } = await useAsyncData('index', () => queryCollection('landing').path('/').first())
|
|
3
|
+
if (!page.value) {
|
|
4
|
+
throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const title = page.value.seo?.title || page.value.title
|
|
8
|
+
const description = page.value.seo?.description || page.value.description
|
|
9
|
+
|
|
10
|
+
useSeoMeta({
|
|
11
|
+
title,
|
|
12
|
+
description,
|
|
13
|
+
ogTitle: title,
|
|
14
|
+
ogDescription: description,
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
if (page.value?.seo?.ogImage) {
|
|
18
|
+
useSeoMeta({
|
|
19
|
+
ogImage: page.value.seo.ogImage,
|
|
20
|
+
twitterImage: page.value.seo.ogImage,
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
defineOgImageComponent('Landing', {
|
|
25
|
+
title,
|
|
26
|
+
description,
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<ContentRenderer
|
|
33
|
+
v-if="page"
|
|
34
|
+
:value="page"
|
|
35
|
+
:prose="false"
|
|
36
|
+
/>
|
|
37
|
+
</template>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { defineContentConfig, defineCollection, z } from '@nuxt/content'
|
|
2
|
+
|
|
3
|
+
export default defineContentConfig({
|
|
4
|
+
collections: {
|
|
5
|
+
landing: defineCollection({
|
|
6
|
+
type: 'page',
|
|
7
|
+
source: {
|
|
8
|
+
// @ts-expect-error __DOCS_DIR__ is not defined
|
|
9
|
+
cwd: globalThis.__DOCS_DIR__,
|
|
10
|
+
include: 'index.md',
|
|
11
|
+
},
|
|
12
|
+
}),
|
|
13
|
+
docs: defineCollection({
|
|
14
|
+
type: 'page',
|
|
15
|
+
source: {
|
|
16
|
+
// @ts-expect-error __DOCS_DIR__ is not defined
|
|
17
|
+
cwd: globalThis.__DOCS_DIR__,
|
|
18
|
+
include: '**',
|
|
19
|
+
exclude: ['index.md'],
|
|
20
|
+
},
|
|
21
|
+
schema: z.object({
|
|
22
|
+
links: z.array(z.object({
|
|
23
|
+
label: z.string(),
|
|
24
|
+
icon: z.string(),
|
|
25
|
+
to: z.string(),
|
|
26
|
+
target: z.string().optional(),
|
|
27
|
+
})).optional(),
|
|
28
|
+
}),
|
|
29
|
+
}),
|
|
30
|
+
},
|
|
31
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Flag enabled when developing docs theme
|
|
2
|
+
const dev = !!process.env.NUXT_DOCS_DEV
|
|
3
|
+
|
|
4
|
+
export default defineNuxtConfig({
|
|
5
|
+
modules: [
|
|
6
|
+
'@nuxt/ui-pro',
|
|
7
|
+
'@nuxt/content',
|
|
8
|
+
'@nuxt/image',
|
|
9
|
+
'@nuxtjs/robots',
|
|
10
|
+
'nuxt-og-image',
|
|
11
|
+
],
|
|
12
|
+
devtools: {
|
|
13
|
+
enabled: dev,
|
|
14
|
+
},
|
|
15
|
+
css: ['../app/assets/css/main.css'],
|
|
16
|
+
future: {
|
|
17
|
+
compatibilityVersion: 4,
|
|
18
|
+
},
|
|
19
|
+
nitro: {
|
|
20
|
+
prerender: {
|
|
21
|
+
routes: ['/'],
|
|
22
|
+
crawlLinks: true,
|
|
23
|
+
failOnError: false,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
icon: {
|
|
27
|
+
provider: 'iconify',
|
|
28
|
+
},
|
|
29
|
+
})
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { field, group } from '@nuxt/content/preview'
|
|
2
|
+
|
|
3
|
+
export default defineNuxtSchema({
|
|
4
|
+
appConfig: {
|
|
5
|
+
ui: group({
|
|
6
|
+
title: 'UI',
|
|
7
|
+
description: 'UI Customization.',
|
|
8
|
+
icon: 'i-lucide-palette',
|
|
9
|
+
fields: {
|
|
10
|
+
colors: group({
|
|
11
|
+
title: 'Colors',
|
|
12
|
+
description: 'Manage main colors of your application',
|
|
13
|
+
icon: 'i-lucide-palette',
|
|
14
|
+
fields: {
|
|
15
|
+
primary: field({
|
|
16
|
+
type: 'string',
|
|
17
|
+
title: 'Primary',
|
|
18
|
+
description: 'Primary color of your UI.',
|
|
19
|
+
icon: 'i-lucide-palette',
|
|
20
|
+
default: 'green',
|
|
21
|
+
required: ['red', 'orange', 'amber', 'yellow', 'lime', 'green', 'emerald', 'teal', 'cyan', 'sky', 'blue', 'indigo', 'violet', 'purple', 'fuchsia', 'pink', 'rose'],
|
|
22
|
+
}),
|
|
23
|
+
neutral: field({
|
|
24
|
+
type: 'string',
|
|
25
|
+
title: 'Neutral',
|
|
26
|
+
description: 'Neutral color of your UI.',
|
|
27
|
+
icon: 'i-lucide-palette',
|
|
28
|
+
default: 'slate',
|
|
29
|
+
required: ['slate', 'gray', 'zinc', 'neutral', 'stone'],
|
|
30
|
+
}),
|
|
31
|
+
},
|
|
32
|
+
}),
|
|
33
|
+
icons: group({
|
|
34
|
+
title: 'Icons',
|
|
35
|
+
description: 'Manage icons used in the application.',
|
|
36
|
+
icon: 'i-lucide-settings',
|
|
37
|
+
fields: {
|
|
38
|
+
search: field({
|
|
39
|
+
type: 'icon',
|
|
40
|
+
title: 'Search Bar',
|
|
41
|
+
description: 'Icon to display in the search bar.',
|
|
42
|
+
icon: 'i-lucide-search',
|
|
43
|
+
default: 'i-lucide-search',
|
|
44
|
+
}),
|
|
45
|
+
dark: field({
|
|
46
|
+
type: 'icon',
|
|
47
|
+
title: 'Dark mode',
|
|
48
|
+
description: 'Icon of color mode button for dark mode.',
|
|
49
|
+
icon: 'i-lucide-moon',
|
|
50
|
+
default: 'i-lucide-moon',
|
|
51
|
+
}),
|
|
52
|
+
light: field({
|
|
53
|
+
type: 'icon',
|
|
54
|
+
title: 'Light mode',
|
|
55
|
+
description: 'Icon of color mode button for light mode.',
|
|
56
|
+
icon: 'i-lucide-sun',
|
|
57
|
+
default: 'i-lucide-sun',
|
|
58
|
+
}),
|
|
59
|
+
external: field({
|
|
60
|
+
type: 'icon',
|
|
61
|
+
title: 'External Link',
|
|
62
|
+
description: 'Icon for external link.',
|
|
63
|
+
icon: 'i-lucide-external-link',
|
|
64
|
+
default: 'i-lucide-external-link',
|
|
65
|
+
}),
|
|
66
|
+
chevron: field({
|
|
67
|
+
type: 'icon',
|
|
68
|
+
title: 'Chevron',
|
|
69
|
+
description: 'Icon for chevron.',
|
|
70
|
+
icon: 'i-lucide-chevron-down',
|
|
71
|
+
default: 'i-lucide-chevron-down',
|
|
72
|
+
}),
|
|
73
|
+
hash: field({
|
|
74
|
+
type: 'icon',
|
|
75
|
+
title: 'Hash',
|
|
76
|
+
description: 'Icon for hash anchors.',
|
|
77
|
+
icon: 'i-lucide-hash',
|
|
78
|
+
default: 'i-lucide-hash',
|
|
79
|
+
}),
|
|
80
|
+
},
|
|
81
|
+
}),
|
|
82
|
+
},
|
|
83
|
+
}),
|
|
84
|
+
seo: group({
|
|
85
|
+
title: 'SEO',
|
|
86
|
+
description: 'SEO configuration.',
|
|
87
|
+
icon: 'i-lucide-search',
|
|
88
|
+
fields: {
|
|
89
|
+
title: field({
|
|
90
|
+
type: 'string',
|
|
91
|
+
title: 'Title',
|
|
92
|
+
description: 'Title to display in the header.',
|
|
93
|
+
icon: 'i-lucide-type',
|
|
94
|
+
default: '',
|
|
95
|
+
}),
|
|
96
|
+
description: field({
|
|
97
|
+
type: 'string',
|
|
98
|
+
title: 'Description',
|
|
99
|
+
description: 'Description to display in the header.',
|
|
100
|
+
icon: 'i-lucide-type',
|
|
101
|
+
default: '',
|
|
102
|
+
}),
|
|
103
|
+
},
|
|
104
|
+
}),
|
|
105
|
+
header: group({
|
|
106
|
+
title: 'Header',
|
|
107
|
+
description: 'Header configuration.',
|
|
108
|
+
icon: 'i-lucide-layout',
|
|
109
|
+
fields: {
|
|
110
|
+
title: field({
|
|
111
|
+
type: 'string',
|
|
112
|
+
title: 'Title',
|
|
113
|
+
description: 'Title to display in the header.',
|
|
114
|
+
icon: 'i-lucide-type',
|
|
115
|
+
default: '',
|
|
116
|
+
}),
|
|
117
|
+
logo: group({
|
|
118
|
+
title: 'Logo',
|
|
119
|
+
description: 'Header logo configuration.',
|
|
120
|
+
icon: 'i-lucide-image',
|
|
121
|
+
fields: {
|
|
122
|
+
light: field({
|
|
123
|
+
type: 'media',
|
|
124
|
+
title: 'Light Mode Logo',
|
|
125
|
+
description: 'Pick an image from your gallery.',
|
|
126
|
+
icon: 'i-lucide-sun',
|
|
127
|
+
default: '',
|
|
128
|
+
}),
|
|
129
|
+
dark: field({
|
|
130
|
+
type: 'media',
|
|
131
|
+
title: 'Dark Mode Logo',
|
|
132
|
+
description: 'Pick an image from your gallery.',
|
|
133
|
+
icon: 'i-lucide-moon',
|
|
134
|
+
default: '',
|
|
135
|
+
}),
|
|
136
|
+
alt: field({
|
|
137
|
+
type: 'string',
|
|
138
|
+
title: 'Alt',
|
|
139
|
+
description: 'Alt to display for accessibility.',
|
|
140
|
+
icon: 'i-lucide-text',
|
|
141
|
+
default: '',
|
|
142
|
+
}),
|
|
143
|
+
},
|
|
144
|
+
}),
|
|
145
|
+
},
|
|
146
|
+
}),
|
|
147
|
+
socials: field({
|
|
148
|
+
type: 'object',
|
|
149
|
+
title: 'Social Networks',
|
|
150
|
+
description: 'Social links configuration.',
|
|
151
|
+
icon: 'i-lucide-network',
|
|
152
|
+
default: {},
|
|
153
|
+
}),
|
|
154
|
+
toc: group({
|
|
155
|
+
title: 'Table of contents',
|
|
156
|
+
description: 'TOC configuration.',
|
|
157
|
+
icon: 'i-lucide-list',
|
|
158
|
+
fields: {
|
|
159
|
+
title: field({
|
|
160
|
+
type: 'string',
|
|
161
|
+
title: 'Title',
|
|
162
|
+
description: 'Title of the table of contents.',
|
|
163
|
+
icon: 'i-lucide-heading',
|
|
164
|
+
default: 'On this page',
|
|
165
|
+
}),
|
|
166
|
+
bottom: group({
|
|
167
|
+
title: 'Bottom',
|
|
168
|
+
description: 'Bottom section of the table of contents.',
|
|
169
|
+
icon: 'i-lucide-list',
|
|
170
|
+
fields: {
|
|
171
|
+
title: field({
|
|
172
|
+
type: 'string',
|
|
173
|
+
title: 'Title',
|
|
174
|
+
description: 'Title of the bottom section.',
|
|
175
|
+
icon: 'i-lucide-heading',
|
|
176
|
+
default: 'Community',
|
|
177
|
+
}),
|
|
178
|
+
links: field({
|
|
179
|
+
type: 'array',
|
|
180
|
+
title: 'Links',
|
|
181
|
+
description: 'Links to display in the bottom section.',
|
|
182
|
+
icon: 'i-lucide-link',
|
|
183
|
+
default: [],
|
|
184
|
+
}),
|
|
185
|
+
},
|
|
186
|
+
}),
|
|
187
|
+
},
|
|
188
|
+
}),
|
|
189
|
+
github: group({
|
|
190
|
+
title: 'GitHub',
|
|
191
|
+
description: 'GitHub configuration.',
|
|
192
|
+
icon: 'i-lucide-github',
|
|
193
|
+
fields: {
|
|
194
|
+
url: field({
|
|
195
|
+
type: 'string',
|
|
196
|
+
title: 'URL',
|
|
197
|
+
description: 'GitHub URL.',
|
|
198
|
+
icon: 'i-lucide-github',
|
|
199
|
+
default: '',
|
|
200
|
+
}),
|
|
201
|
+
},
|
|
202
|
+
}),
|
|
203
|
+
},
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
declare module '@nuxt/schema' {
|
|
207
|
+
interface AppConfig {
|
|
208
|
+
seo: {
|
|
209
|
+
titleTemplate: string
|
|
210
|
+
title: string
|
|
211
|
+
description: string
|
|
212
|
+
}
|
|
213
|
+
ui: {
|
|
214
|
+
colors: {
|
|
215
|
+
primary: string
|
|
216
|
+
neutral: string
|
|
217
|
+
}
|
|
218
|
+
icons: {
|
|
219
|
+
search: string
|
|
220
|
+
dark: string
|
|
221
|
+
light: string
|
|
222
|
+
external: string
|
|
223
|
+
chevron: string
|
|
224
|
+
hash: string
|
|
225
|
+
} & Record<string, string>
|
|
226
|
+
}
|
|
227
|
+
header: {
|
|
228
|
+
title: string
|
|
229
|
+
logo: {
|
|
230
|
+
light: string
|
|
231
|
+
dark: string
|
|
232
|
+
alt: string
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
socials: Record<string, string>
|
|
236
|
+
toc: {
|
|
237
|
+
title: string
|
|
238
|
+
bottom: {
|
|
239
|
+
title: string
|
|
240
|
+
links: {
|
|
241
|
+
icon: string
|
|
242
|
+
label: string
|
|
243
|
+
to: string
|
|
244
|
+
target: string
|
|
245
|
+
}[]
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
github: {
|
|
249
|
+
owner: string
|
|
250
|
+
name: string
|
|
251
|
+
url: string
|
|
252
|
+
branch: string
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { stringify } from 'minimark/stringify'
|
|
2
|
+
import { withLeadingSlash } from 'ufo'
|
|
3
|
+
|
|
4
|
+
export default eventHandler(async (event) => {
|
|
5
|
+
const slug = getRouterParams(event)['slug.md']
|
|
6
|
+
if (!slug?.endsWith('.md')) {
|
|
7
|
+
throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const path = withLeadingSlash(slug.replace('.md', ''))
|
|
11
|
+
const page = await queryCollection(event, 'docs').path(path).first()
|
|
12
|
+
if (!page) {
|
|
13
|
+
throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Add title and description to the top of the page if missing
|
|
17
|
+
if (page.body.value[0]?.[0] !== 'h1') {
|
|
18
|
+
page.body.value.unshift(['blockquote', {}, page.description])
|
|
19
|
+
page.body.value.unshift(['h1', {}, page.title])
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
setHeader(event, 'Content-Type', 'text/markdown; charset=utf-8')
|
|
23
|
+
return stringify({ ...page.body, type: 'minimark' }, { format: 'markdown/html' })
|
|
24
|
+
})
|