undocs 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/app.config.ts +1 -3
- package/app/components/AppFooterNotes.vue +2 -4
- package/app/components/AppHeader.vue +0 -1
- package/app/components/SocialButtons.vue +26 -24
- package/app/composables/useSponsors.ts +16 -0
- package/app/layouts/blog.vue +9 -0
- package/app/layouts/docs.vue +1 -1
- package/app/modules/content/hooks.ts +8 -7
- package/app/modules/content/icons.ts +4 -0
- package/app/pages/[...slug].vue +12 -55
- package/app/pages/blog/[...slug].vue +63 -0
- package/app/pages/blog/index.vue +57 -0
- package/app/pages/index.vue +61 -3
- package/package.json +1 -1
- package/schema/config.d.ts +2 -0
- package/schema/config.json +16 -0
package/app/app.config.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
const appConfig = useAppConfig()
|
|
3
|
-
|
|
4
|
-
console.log(JSON.stringify(appConfig.docs, null, 2))
|
|
5
3
|
</script>
|
|
6
4
|
|
|
7
5
|
<template>
|
|
@@ -19,7 +17,7 @@ console.log(JSON.stringify(appConfig.docs, null, 2))
|
|
|
19
17
|
· Generated with
|
|
20
18
|
<NuxtLink class="font-medium hover:text-primary" to="https://undocs.unjs.io" target="_blank">UnDocs </NuxtLink>
|
|
21
19
|
and
|
|
22
|
-
<NuxtLink class="font-medium hover:text-primary" to="https://ui.nuxt.com/pro" target="_blank">Nuxt UI Pro</NuxtLink
|
|
23
|
-
|
|
20
|
+
<NuxtLink class="font-medium hover:text-primary" to="https://ui.nuxt.com/pro" target="_blank">Nuxt UI Pro</NuxtLink>
|
|
21
|
+
.
|
|
24
22
|
</p>
|
|
25
23
|
</template>
|
|
@@ -35,7 +35,6 @@ const headerLinks = computed(() => {
|
|
|
35
35
|
<UTooltip text="Search" :kbds="['meta', 'K']">
|
|
36
36
|
<UContentSearchButton />
|
|
37
37
|
</UTooltip>
|
|
38
|
-
<!-- <div class="flex items-center border-l border-slate-200 ml-6 pl-6 dark:border-slate-800"> -->
|
|
39
38
|
<UColorModeButton />
|
|
40
39
|
<SocialButtons />
|
|
41
40
|
</template>
|
|
@@ -1,33 +1,35 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
const appConfig = useAppConfig()
|
|
3
3
|
|
|
4
|
-
const props = defineProps({
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
})
|
|
4
|
+
// const props = defineProps({
|
|
5
|
+
// socials: {
|
|
6
|
+
// type: Array,
|
|
7
|
+
// required: false,
|
|
8
|
+
// default: () => ['github', 'twitter', 'discord'],
|
|
9
|
+
// },
|
|
10
|
+
// })
|
|
11
11
|
|
|
12
12
|
const socialLinks = computed(() => {
|
|
13
|
-
return
|
|
14
|
-
.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
13
|
+
return (
|
|
14
|
+
Object.entries({ github: appConfig.docs.github, ...appConfig.docs.socials })
|
|
15
|
+
.reverse() // x<>github
|
|
16
|
+
// .filter(([key]) => props.socials?.includes(key) || !props.socials)
|
|
17
|
+
.map(([key, value]) => {
|
|
18
|
+
if (typeof value === 'object') {
|
|
19
|
+
return value
|
|
20
|
+
}
|
|
21
|
+
if (typeof value === 'string' && value) {
|
|
22
|
+
return {
|
|
23
|
+
// Workaround: i-simple-icons-x i-simple-icons-github
|
|
24
|
+
icon: `i-simple-icons-${key}`,
|
|
25
|
+
label: value,
|
|
26
|
+
to: /^https?:\/\//.test(value) ? value : `https://${key}.com/${value}`,
|
|
27
|
+
}
|
|
26
28
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
return undefined
|
|
30
|
+
})
|
|
31
|
+
.filter(Boolean)
|
|
32
|
+
)
|
|
31
33
|
})
|
|
32
34
|
</script>
|
|
33
35
|
<template>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface Sponsors {
|
|
2
|
+
username: string
|
|
3
|
+
sponsors: {
|
|
4
|
+
name: string
|
|
5
|
+
image: string
|
|
6
|
+
website: string
|
|
7
|
+
}[][]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function useSponsors(): Promise<Sponsors | undefined> {
|
|
11
|
+
const appConfig = useAppConfig()
|
|
12
|
+
const sponsorsAPI = appConfig.docs.sponsors.api
|
|
13
|
+
if (sponsorsAPI) {
|
|
14
|
+
return (await $fetch<Sponsors>(sponsorsAPI)) || undefined
|
|
15
|
+
}
|
|
16
|
+
}
|
package/app/layouts/docs.vue
CHANGED
|
@@ -7,7 +7,7 @@ const docsNav = useDocsNav()
|
|
|
7
7
|
<UPage :ui="{ left: 'lg:col-span-2 pr-2 border-r border-default' }">
|
|
8
8
|
<template #left>
|
|
9
9
|
<UPageAside>
|
|
10
|
-
<UPageAnchors :links="docsNav.links" />
|
|
10
|
+
<UPageAnchors :links="docsNav.links.filter((l) => l.title !== 'Blog')" />
|
|
11
11
|
<USeparator v-if="docsNav.activeLinks?.length" type="dashed" class="py-6" />
|
|
12
12
|
<UContentNavigation
|
|
13
13
|
:navigation="docsNav.activeLinks"
|
|
@@ -129,14 +129,15 @@ function removeFirstH1AndBlockquote(body: MarkdownRoot, content: ParsedContentFi
|
|
|
129
129
|
|
|
130
130
|
// Use the first blockquote as the description
|
|
131
131
|
const firstEl = body.value[0]
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
132
|
+
if (firstEl) {
|
|
133
|
+
const bloquoteText = _getTextContent(firstEl)
|
|
134
|
+
if (firstEl[0] === 'blockquote' && content.description === '' && !bloquoteText.startsWith('!')) {
|
|
135
|
+
content.description = bloquoteText
|
|
136
|
+
if (content.seo) {
|
|
137
|
+
;(content.seo as any).description = bloquoteText
|
|
138
|
+
}
|
|
139
|
+
body.value.shift()
|
|
138
140
|
}
|
|
139
|
-
body.value.shift()
|
|
140
141
|
}
|
|
141
142
|
}
|
|
142
143
|
|
package/app/pages/[...slug].vue
CHANGED
|
@@ -33,71 +33,28 @@ usePageSEO({
|
|
|
33
33
|
description: page.value?.description,
|
|
34
34
|
})
|
|
35
35
|
|
|
36
|
-
const headline = computed(() => findPageHeadline(page.value))
|
|
37
|
-
|
|
38
|
-
const tocLinks = computed(() => {
|
|
39
|
-
return (page.value.body?.toc?.links || []).map((link) => ({
|
|
40
|
-
...link,
|
|
41
|
-
children: undefined,
|
|
42
|
-
}))
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
const tocMobileOpen = ref(false)
|
|
46
|
-
|
|
47
|
-
const tocMobileLinks = computed(() => {
|
|
48
|
-
return [
|
|
49
|
-
[
|
|
50
|
-
{
|
|
51
|
-
label: 'Return to top',
|
|
52
|
-
click: () => {
|
|
53
|
-
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
],
|
|
57
|
-
(page.value.body?.toc?.links || []).map((link) => ({
|
|
58
|
-
label: link.text,
|
|
59
|
-
// href: `#${link.id}`,
|
|
60
|
-
click: () => {
|
|
61
|
-
tocMobileOpen.value = false
|
|
62
|
-
document.getElementById(link.id)?.scrollIntoView({ behavior: 'smooth' })
|
|
63
|
-
},
|
|
64
|
-
})),
|
|
65
|
-
]
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
const isMobile = ref(false)
|
|
69
|
-
|
|
70
36
|
onMounted(() => {
|
|
71
|
-
|
|
37
|
+
const hash = window.location.hash
|
|
38
|
+
if (hash) {
|
|
39
|
+
document.querySelector(hash)?.scrollIntoView()
|
|
40
|
+
}
|
|
72
41
|
})
|
|
73
42
|
</script>
|
|
74
43
|
|
|
75
44
|
<template>
|
|
76
45
|
<UPage v-if="page">
|
|
77
|
-
<UPageHeader
|
|
46
|
+
<UPageHeader
|
|
47
|
+
:title="page.title"
|
|
48
|
+
:description="page.description"
|
|
49
|
+
:links="page.links"
|
|
50
|
+
:headline="findPageHeadline(page)"
|
|
51
|
+
/>
|
|
78
52
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<template v-if="tocLinks.length > 0" #right>
|
|
82
|
-
<UContentToc title="On this page" :links="tocLinks" class="hidden lg:block" />
|
|
53
|
+
<template #right>
|
|
54
|
+
<UContentToc title="On this page" :links="page.body?.toc?.links || []" highlight />
|
|
83
55
|
</template>
|
|
84
|
-
<!-- mobile -->
|
|
85
|
-
<div
|
|
86
|
-
v-if="tocMobileLinks.length > 1"
|
|
87
|
-
class="float-right mt-4 top-[calc(var(--header-height)_+_0.5rem)] z-10 flex justify-end sticky mb-2 lg:hidden"
|
|
88
|
-
>
|
|
89
|
-
<UDropdownMenu v-model:open="tocMobileOpen" :items="tocMobileLinks" :mode="isMobile ? 'click' : 'hover'">
|
|
90
|
-
<UButton
|
|
91
|
-
color="neutral"
|
|
92
|
-
label="On this page"
|
|
93
|
-
:trailing="false"
|
|
94
|
-
:icon="`i-heroicons-chevron-${tocMobileOpen ? 'down' : 'left'}-20-solid`"
|
|
95
|
-
/>
|
|
96
|
-
</UDropdownMenu>
|
|
97
|
-
</div>
|
|
98
56
|
|
|
99
57
|
<UPageBody prose class="break-words">
|
|
100
|
-
<br v-if="tocMobileLinks.length > 1" class="lg:hidden mb-2" />
|
|
101
58
|
<ContentRenderer v-if="page.body" :value="page" />
|
|
102
59
|
|
|
103
60
|
<div class="space-y-6">
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { kebabCase } from 'scule'
|
|
3
|
+
|
|
4
|
+
definePageMeta({
|
|
5
|
+
layout: 'blog',
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
const route = useRoute()
|
|
9
|
+
|
|
10
|
+
const { data: page } = await useAsyncData(kebabCase(route.path), () =>
|
|
11
|
+
queryCollection('content').path(route.path).first(),
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
if (!page.value) {
|
|
15
|
+
throw createError({
|
|
16
|
+
statusCode: 404,
|
|
17
|
+
statusMessage: 'Page not found',
|
|
18
|
+
message: `${route.path} does not exist`,
|
|
19
|
+
fatal: true,
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const appConfig = useAppConfig()
|
|
24
|
+
|
|
25
|
+
usePageSEO({
|
|
26
|
+
title: `${page.value?.title} - ${appConfig.site.name}`,
|
|
27
|
+
ogTitle: page.value?.title,
|
|
28
|
+
description: page.value?.description,
|
|
29
|
+
})
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<UPage v-if="page">
|
|
34
|
+
<UPageHeader v-bind="page" :ui="{ headline: 'flex flex-col gap-y-8 items-start' }">
|
|
35
|
+
<template #headline>
|
|
36
|
+
<UBreadcrumb
|
|
37
|
+
:items="[{ label: 'Blog', icon: 'i-lucide-newspaper', to: '/blog' }, { label: page.title }]"
|
|
38
|
+
class="max-w-full"
|
|
39
|
+
/>
|
|
40
|
+
<div class="flex items-center space-x-2">
|
|
41
|
+
<span>
|
|
42
|
+
{{ page.meta.category }}
|
|
43
|
+
</span>
|
|
44
|
+
<span class="text-muted"
|
|
45
|
+
> · <time>{{ page.meta.date }}</time></span
|
|
46
|
+
>
|
|
47
|
+
</div>
|
|
48
|
+
</template>
|
|
49
|
+
<div class="mt-4 flex flex-wrap items-center gap-6">
|
|
50
|
+
<UUser
|
|
51
|
+
v-for="(author, index) in page.meta.authors || []"
|
|
52
|
+
:key="index"
|
|
53
|
+
:name="author.name"
|
|
54
|
+
:avatar="{ src: `https://github.com/${author.github}.png?size=64` }"
|
|
55
|
+
:to="`https://github.com/${author.github}`"
|
|
56
|
+
target="_blank"
|
|
57
|
+
:description="author.to ? `@${author.to.split('/').pop()}` : undefined"
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
60
|
+
</UPageHeader>
|
|
61
|
+
<ContentRenderer v-if="page.body" :value="page" />
|
|
62
|
+
</UPage>
|
|
63
|
+
</template>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const { data: page } = await useAsyncData('/blog', () => queryCollection('content').path('/blog').first())
|
|
3
|
+
|
|
4
|
+
if (!page.value) {
|
|
5
|
+
throw createError({
|
|
6
|
+
statusCode: 404,
|
|
7
|
+
statusMessage: 'Page not found',
|
|
8
|
+
message: '/blog does not exist',
|
|
9
|
+
fatal: true,
|
|
10
|
+
})
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { data: articles } = await useAsyncData(() =>
|
|
14
|
+
queryCollection('content')
|
|
15
|
+
.where('path', 'LIKE', '/blog/%')
|
|
16
|
+
.order('id', 'DESC')
|
|
17
|
+
.all()
|
|
18
|
+
.then((res) => res),
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
const appConfig = useAppConfig()
|
|
22
|
+
|
|
23
|
+
usePageSEO({
|
|
24
|
+
title: `${page.value?.title} - ${appConfig.site.name}`,
|
|
25
|
+
ogTitle: page.value?.title,
|
|
26
|
+
description: page.value?.description,
|
|
27
|
+
})
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<template>
|
|
31
|
+
<UContainer v-if="page">
|
|
32
|
+
<UPageHero :title="page.title" orientation="horizontal">
|
|
33
|
+
<template #description>{{ page.description }}</template>
|
|
34
|
+
</UPageHero>
|
|
35
|
+
|
|
36
|
+
<UPageBody>
|
|
37
|
+
<UContainer>
|
|
38
|
+
<UBlogPosts class="mb-12 md:grid-cols-2 lg:grid-cols-3">
|
|
39
|
+
<UBlogPost
|
|
40
|
+
v-for="(article, index) in articles"
|
|
41
|
+
:key="article.path"
|
|
42
|
+
:to="article.path"
|
|
43
|
+
:title="article.title"
|
|
44
|
+
:description="article.description"
|
|
45
|
+
:date="article.meta?.date"
|
|
46
|
+
:badge="
|
|
47
|
+
article.meta?.category ? { label: article.meta.category, color: 'primary', variant: 'subtle' } : undefined
|
|
48
|
+
"
|
|
49
|
+
:variant="index > 0 ? 'outline' : 'subtle'"
|
|
50
|
+
:orientation2="index === 0 ? 'horizontal' : 'vertical'"
|
|
51
|
+
:class="[index === 0 && 'col-span-full']"
|
|
52
|
+
/>
|
|
53
|
+
</UBlogPosts>
|
|
54
|
+
</UContainer>
|
|
55
|
+
</UPageBody>
|
|
56
|
+
</UContainer>
|
|
57
|
+
</template>
|
package/app/pages/index.vue
CHANGED
|
@@ -8,8 +8,6 @@ const appConfig = useAppConfig()
|
|
|
8
8
|
|
|
9
9
|
const docsConfig = appConfig.docs as DocsConfig
|
|
10
10
|
|
|
11
|
-
// console.log('docsConfig', JSON.stringify(docsConfig, null, 2))
|
|
12
|
-
|
|
13
11
|
const landing: LandingConfig & { _github: string } = defu(docsConfig.landing || {}, {
|
|
14
12
|
// Meta
|
|
15
13
|
navigation: false,
|
|
@@ -90,6 +88,16 @@ const hero = computed(() => {
|
|
|
90
88
|
code: landing!.heroCode,
|
|
91
89
|
} as const
|
|
92
90
|
})
|
|
91
|
+
|
|
92
|
+
const { data: latest } = await useAsyncData(() =>
|
|
93
|
+
queryCollection('content')
|
|
94
|
+
.where('path', 'LIKE', '/blog/%')
|
|
95
|
+
.order('id', 'DESC')
|
|
96
|
+
.first()
|
|
97
|
+
.then((res) => res),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
const { data: sponsors } = await useAsyncData(() => useSponsors())
|
|
93
101
|
</script>
|
|
94
102
|
|
|
95
103
|
<template>
|
|
@@ -109,6 +117,18 @@ const hero = computed(() => {
|
|
|
109
117
|
<LandingBackground />
|
|
110
118
|
</template>
|
|
111
119
|
|
|
120
|
+
<template #headline>
|
|
121
|
+
<NuxtLink v-if="latest" :to="latest.path">
|
|
122
|
+
<UBadge
|
|
123
|
+
variant="subtle"
|
|
124
|
+
size="lg"
|
|
125
|
+
class="px-3 relative rounded-full font-semibold dark:hover:bg-primary-400/15 dark:hover:ring-primary-700"
|
|
126
|
+
>
|
|
127
|
+
{{ latest.title }}
|
|
128
|
+
</UBadge>
|
|
129
|
+
</NuxtLink>
|
|
130
|
+
</template>
|
|
131
|
+
|
|
112
132
|
<template #title>
|
|
113
133
|
{{ landing.heroTitle }}<br /><span class="text-primary text-4xl">{{ landing.heroSubtitle }}</span>
|
|
114
134
|
</template>
|
|
@@ -170,12 +190,50 @@ const hero = computed(() => {
|
|
|
170
190
|
</template>
|
|
171
191
|
</UPageSection>
|
|
172
192
|
|
|
173
|
-
<UPageSection v-if="landing.contributors && landing._github" title="
|
|
193
|
+
<UPageSection v-if="landing.contributors && landing._github" title="💛 Contributors">
|
|
174
194
|
<div class="flex justify-center">
|
|
175
195
|
<a :href="`https://github.com/${landing._github}/graphs/contributors`" target="_blank">
|
|
176
196
|
<img :src="`https://contrib.rocks/image?repo=${landing._github}`" />
|
|
177
197
|
</a>
|
|
178
198
|
</div>
|
|
179
199
|
</UPageSection>
|
|
200
|
+
|
|
201
|
+
<UPageSection v-if="sponsors?.sponsors.length" title="💜 Sponsors">
|
|
202
|
+
<div class="flex flex-col items-center">
|
|
203
|
+
<div
|
|
204
|
+
v-for="(tier, i) of sponsors.sponsors"
|
|
205
|
+
:key="i"
|
|
206
|
+
class="flex flex-wrap justify-center gap-4 mb-6 mt-6 max-w-4xl"
|
|
207
|
+
>
|
|
208
|
+
<div v-for="s in tier" :key="s.name" class="flex items-center gap-2 max-w-[300px]">
|
|
209
|
+
<a
|
|
210
|
+
:href="s.website"
|
|
211
|
+
target="_blank"
|
|
212
|
+
class="flex items-center gap-2"
|
|
213
|
+
:class="`font-size-${i === 0 ? '4xl' : i === 1 ? '3xl' : 'lg'}`"
|
|
214
|
+
>
|
|
215
|
+
<img
|
|
216
|
+
v-if="s.image"
|
|
217
|
+
:src="s.image"
|
|
218
|
+
:alt="s.name"
|
|
219
|
+
class="object-contain rounded-lg"
|
|
220
|
+
:class="`w-${i === 0 ? 16 : 8} h-${i === 0 ? 16 : 8}`"
|
|
221
|
+
/>
|
|
222
|
+
<span v-if="i < 2" class="text-lg font-semibold">{{ s.name }}</span>
|
|
223
|
+
</a>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
227
|
+
<div class="text-center">
|
|
228
|
+
<UButton
|
|
229
|
+
v-if="sponsors.username"
|
|
230
|
+
:to="`https://github.com/sponsors/${sponsors.username}`"
|
|
231
|
+
target="_blank"
|
|
232
|
+
color="neutral"
|
|
233
|
+
>
|
|
234
|
+
Become a Sponsor
|
|
235
|
+
</UButton>
|
|
236
|
+
</div>
|
|
237
|
+
</UPageSection>
|
|
180
238
|
</div>
|
|
181
239
|
</template>
|
package/package.json
CHANGED
package/schema/config.d.ts
CHANGED
|
@@ -5,11 +5,13 @@ export interface DocsConfig {
|
|
|
5
5
|
shortDescription?: string
|
|
6
6
|
url?: string
|
|
7
7
|
github?: string
|
|
8
|
+
socials?: Record<string, string>
|
|
8
9
|
branch?: string
|
|
9
10
|
themeColor?: string
|
|
10
11
|
redirects?: Record<string, string>
|
|
11
12
|
automd?: unknown
|
|
12
13
|
buildCache?: boolean
|
|
14
|
+
sponsors?: { api: string }
|
|
13
15
|
landing?:
|
|
14
16
|
| false
|
|
15
17
|
| {
|
package/schema/config.json
CHANGED
|
@@ -30,6 +30,22 @@
|
|
|
30
30
|
"type": "string",
|
|
31
31
|
"description": "The GitHub repository for the documentation site."
|
|
32
32
|
},
|
|
33
|
+
"socials": {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"description": "Social media links for the documentation site.",
|
|
36
|
+
"additionalProperties": {
|
|
37
|
+
"type": "string"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"sponsors": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"properties": {
|
|
43
|
+
"api": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"description": "The URL to the sponsors JSON API."
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
33
49
|
"branch": {
|
|
34
50
|
"type": "string",
|
|
35
51
|
"description": "The branch of the GitHub repository for the documentation site."
|