undocs 0.3.7 → 0.3.9
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/components/SocialButtons.vue +12 -20
- package/app/content.config.ts +1 -1
- package/app/layouts/docs.vue +1 -7
- package/app/modules/content/hooks.ts +45 -27
- package/app/pages/[...slug].vue +30 -7
- package/package.json +1 -1
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
const appConfig = useAppConfig()
|
|
3
3
|
|
|
4
|
-
// const props = defineProps({
|
|
5
|
-
// socials: {
|
|
6
|
-
// type: Array,
|
|
7
|
-
// required: false,
|
|
8
|
-
// default: () => ['github', 'twitter', 'discord'],
|
|
9
|
-
// },
|
|
10
|
-
// })
|
|
11
|
-
|
|
12
4
|
const socialLinks = computed(() => {
|
|
13
5
|
return (
|
|
14
6
|
Object.entries({ github: appConfig.docs.github, ...appConfig.docs.socials })
|
|
@@ -22,7 +14,7 @@ const socialLinks = computed(() => {
|
|
|
22
14
|
return {
|
|
23
15
|
// Workaround: i-simple-icons-x i-simple-icons-github
|
|
24
16
|
icon: `i-simple-icons-${key}`,
|
|
25
|
-
label:
|
|
17
|
+
label: titleCase(key),
|
|
26
18
|
to: /^https?:\/\//.test(value) ? value : `https://${key}.com/${value}`,
|
|
27
19
|
}
|
|
28
20
|
}
|
|
@@ -33,15 +25,15 @@ const socialLinks = computed(() => {
|
|
|
33
25
|
})
|
|
34
26
|
</script>
|
|
35
27
|
<template>
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
28
|
+
<UTooltip v-for="link of socialLinks" :key="link.label" :text="link.label">
|
|
29
|
+
<UButton
|
|
30
|
+
:aria-label="link.label"
|
|
31
|
+
:icon="link.icon"
|
|
32
|
+
:to="link.to"
|
|
33
|
+
target="_blank"
|
|
34
|
+
rel="noopener noreferrer"
|
|
35
|
+
color="neutral"
|
|
36
|
+
variant="ghost"
|
|
37
|
+
/>
|
|
38
|
+
</UTooltip>
|
|
47
39
|
</template>
|
package/app/content.config.ts
CHANGED
package/app/layouts/docs.vue
CHANGED
|
@@ -9,13 +9,7 @@ const docsNav = useDocsNav()
|
|
|
9
9
|
<UPageAside>
|
|
10
10
|
<UPageAnchors :links="docsNav.links.filter((l) => l.title !== 'Blog')" />
|
|
11
11
|
<USeparator v-if="docsNav.activeLinks?.length" type="dashed" class="py-6" />
|
|
12
|
-
<UContentNavigation
|
|
13
|
-
:navigation="docsNav.activeLinks"
|
|
14
|
-
default-open
|
|
15
|
-
trailing-icon="i-lucide-chevron-right"
|
|
16
|
-
:ui="{ linkTrailingIcon: 'group-data-[state=open]:rotate-90' }"
|
|
17
|
-
highlight
|
|
18
|
-
/>
|
|
12
|
+
<UContentNavigation :navigation="docsNav.activeLinks" :collapsible="false" />
|
|
19
13
|
</UPageAside>
|
|
20
14
|
</template>
|
|
21
15
|
<slot />
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MarkdownRoot,
|
|
1
|
+
import type { MarkdownRoot, MinimarkNode, MinimarkElement } from '@nuxt/content'
|
|
2
2
|
import type { Nuxt } from 'nuxt/schema'
|
|
3
3
|
import { CommonIcons } from './icons'
|
|
4
4
|
import type { DocsConfig } from '../../../schema/config'
|
|
@@ -13,6 +13,9 @@ interface ParsedContentFile extends Record<string, unknown> {
|
|
|
13
13
|
meta?: {
|
|
14
14
|
icon?: string
|
|
15
15
|
}
|
|
16
|
+
body?: {
|
|
17
|
+
icon?: string
|
|
18
|
+
}
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
export async function setupContentHooks(nuxt: Nuxt, docsConfig: DocsConfig) {
|
|
@@ -59,13 +62,14 @@ export async function setupContentHooks(nuxt: Nuxt, docsConfig: DocsConfig) {
|
|
|
59
62
|
})
|
|
60
63
|
|
|
61
64
|
nuxt.hooks.hook('content:file:afterParse', ({ content }) => {
|
|
65
|
+
// Infer icon for top level navigation
|
|
66
|
+
inferIcons(content)
|
|
67
|
+
|
|
62
68
|
// Filter out non-markdown files
|
|
63
69
|
if (content.extension !== 'md') {
|
|
64
70
|
return
|
|
65
71
|
}
|
|
66
72
|
|
|
67
|
-
resolveFileIcon(content)
|
|
68
|
-
|
|
69
73
|
const body = content.body as MarkdownRoot
|
|
70
74
|
removeFirstH1AndBlockquote(body, content)
|
|
71
75
|
body.value = mergeCodeGroups(body.value)
|
|
@@ -84,7 +88,7 @@ export async function setupContentHooks(nuxt: Nuxt, docsConfig: DocsConfig) {
|
|
|
84
88
|
|
|
85
89
|
// Handle GitHub flavoured markdown blockquotes
|
|
86
90
|
// https://github.com/orgs/community/discussions/16925
|
|
87
|
-
function transformGithubAlert(node:
|
|
91
|
+
function transformGithubAlert(node: MinimarkNode) {
|
|
88
92
|
if (
|
|
89
93
|
node[0] === 'blockquote' &&
|
|
90
94
|
Array.isArray(node[2]) &&
|
|
@@ -97,17 +101,17 @@ function transformGithubAlert(node: MinimalNode) {
|
|
|
97
101
|
// @ts-expect-error read-only
|
|
98
102
|
node[0] = node[2][2][2].slice(1).toLowerCase()
|
|
99
103
|
// @ts-expect-error read-only
|
|
100
|
-
node[2] = ['p', {}, ...node[2].slice(3)] as
|
|
104
|
+
node[2] = ['p', {}, ...node[2].slice(3)] as MinimarkElement
|
|
101
105
|
}
|
|
102
106
|
}
|
|
103
107
|
|
|
104
108
|
// --- transform steps list ---
|
|
105
109
|
|
|
106
|
-
function transformStepsList(node:
|
|
110
|
+
function transformStepsList(node: MinimarkNode) {
|
|
107
111
|
// CONVERT OL->LI to Steps
|
|
108
112
|
if (Array.isArray(node) && node[0] === 'ol' && node.length > 3 && Array.isArray(node[2]) && node[2][0] === 'li') {
|
|
109
|
-
const stepsChildren = node.slice(2).map((li:
|
|
110
|
-
return ['h4', {}, ...li.slice(2)] as
|
|
113
|
+
const stepsChildren = node.slice(2).map((li: MinimarkNode) => {
|
|
114
|
+
return ['h4', {}, ...li.slice(2)] as MinimarkElement
|
|
111
115
|
})
|
|
112
116
|
node.splice(0, Infinity, 'steps', { level: '4' }, ...stepsChildren)
|
|
113
117
|
}
|
|
@@ -143,17 +147,31 @@ function removeFirstH1AndBlockquote(body: MarkdownRoot, content: ParsedContentFi
|
|
|
143
147
|
|
|
144
148
|
// --- resolve icon ---
|
|
145
149
|
|
|
146
|
-
function
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
content.
|
|
151
|
-
|
|
150
|
+
function inferIcons(content: ParsedContentFile) {
|
|
151
|
+
const icon = content.meta?.icon || content.navigation?.icon || content.body?.icon || _resolveIcon(content.path)
|
|
152
|
+
|
|
153
|
+
content.body ??= {}
|
|
154
|
+
content.body.icon ??= icon
|
|
155
|
+
|
|
156
|
+
content.meta ??= {}
|
|
157
|
+
content.meta.icon ??= icon
|
|
158
|
+
|
|
159
|
+
content.navigation ??= {}
|
|
160
|
+
content.navigation.icon ??= icon
|
|
152
161
|
}
|
|
153
162
|
|
|
154
163
|
function _resolveIcon(path: string = '') {
|
|
155
164
|
// Split the path into parts and reverse it
|
|
156
|
-
const paths = path
|
|
165
|
+
const paths = path
|
|
166
|
+
.slice(1)
|
|
167
|
+
.split('/')
|
|
168
|
+
.reverse()
|
|
169
|
+
.filter((p) => p && !p.startsWith('.'))
|
|
170
|
+
|
|
171
|
+
// Skip 2+ levels of directories
|
|
172
|
+
if (paths.length > 2) {
|
|
173
|
+
return
|
|
174
|
+
}
|
|
157
175
|
|
|
158
176
|
// Search for icons in reverse order
|
|
159
177
|
for (const p of paths) {
|
|
@@ -167,10 +185,10 @@ function _resolveIcon(path: string = '') {
|
|
|
167
185
|
|
|
168
186
|
// --- Merge code groups ---
|
|
169
187
|
|
|
170
|
-
function mergeCodeGroups(children:
|
|
171
|
-
const newChildren:
|
|
188
|
+
function mergeCodeGroups(children: MinimarkNode[] = []): MinimarkNode[] {
|
|
189
|
+
const newChildren: MinimarkNode[] = []
|
|
172
190
|
|
|
173
|
-
let codeBlocks:
|
|
191
|
+
let codeBlocks: MinimarkNode[] = []
|
|
174
192
|
|
|
175
193
|
for (let i = 0; i < children.length; i++) {
|
|
176
194
|
if (_isNamedCodeBlock(children[i])) {
|
|
@@ -187,20 +205,20 @@ function mergeCodeGroups(children: MinimalNode[] = []): MinimalNode[] {
|
|
|
187
205
|
return newChildren
|
|
188
206
|
}
|
|
189
207
|
|
|
190
|
-
function _isNamedCodeBlock(node:
|
|
208
|
+
function _isNamedCodeBlock(node: MinimarkNode): boolean {
|
|
191
209
|
return node[0] === 'pre' && (node[1] as any)?.filename && node[2]?.[0] === 'code'
|
|
192
210
|
}
|
|
193
211
|
|
|
194
212
|
// --- transform automd jsdocs ---
|
|
195
213
|
|
|
196
|
-
export function transformJSDocs(currChildIdx: number, children:
|
|
214
|
+
export function transformJSDocs(currChildIdx: number, children: MinimarkNode[] = []) {
|
|
197
215
|
if (!children?.length || !_isJSDocBlock(children[currChildIdx])) {
|
|
198
216
|
return
|
|
199
217
|
}
|
|
200
218
|
|
|
201
|
-
const fields:
|
|
219
|
+
const fields: MinimarkNode[] = []
|
|
202
220
|
|
|
203
|
-
const generateFields = (i: number):
|
|
221
|
+
const generateFields = (i: number): MinimarkNode => {
|
|
204
222
|
const name = _parseJSDocName(children[i])
|
|
205
223
|
const type = _parseJSDocType(children[i + 1])
|
|
206
224
|
|
|
@@ -212,7 +230,7 @@ export function transformJSDocs(currChildIdx: number, children: MinimalNode[] =
|
|
|
212
230
|
type,
|
|
213
231
|
}
|
|
214
232
|
|
|
215
|
-
const content:
|
|
233
|
+
const content: MinimarkNode[] = []
|
|
216
234
|
|
|
217
235
|
i++
|
|
218
236
|
|
|
@@ -263,17 +281,17 @@ export function transformJSDocs(currChildIdx: number, children: MinimalNode[] =
|
|
|
263
281
|
}
|
|
264
282
|
}
|
|
265
283
|
|
|
266
|
-
function _isJSDocBlock(children:
|
|
284
|
+
function _isJSDocBlock(children: MinimarkNode): boolean {
|
|
267
285
|
return (
|
|
268
286
|
children?.tag === 'h3' && children?.children?.[0]?.tag === 'code' && children?.children?.[0]?.type === 'element'
|
|
269
287
|
)
|
|
270
288
|
}
|
|
271
289
|
|
|
272
|
-
function _parseJSDocName(node:
|
|
290
|
+
function _parseJSDocName(node: MinimarkNode): string {
|
|
273
291
|
// Code block || id prop || empty string
|
|
274
292
|
return node.children?.[0]?.children?.[0]?.value || node?.props?.id || ''
|
|
275
293
|
}
|
|
276
|
-
function _parseJSDocType(node:
|
|
294
|
+
function _parseJSDocType(node: MinimarkNode): string {
|
|
277
295
|
const hasType = !!node?.children?.[0]?.children?.[0]?.children?.[0]?.value
|
|
278
296
|
if (!hasType) {
|
|
279
297
|
return ''
|
|
@@ -284,7 +302,7 @@ function _parseJSDocType(node: MinimalNode): string {
|
|
|
284
302
|
|
|
285
303
|
// --- internal utils ---
|
|
286
304
|
|
|
287
|
-
function _getTextContent(node:
|
|
305
|
+
function _getTextContent(node: MinimarkNode): string {
|
|
288
306
|
if (!node) {
|
|
289
307
|
return ''
|
|
290
308
|
}
|
package/app/pages/[...slug].vue
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { kebabCase } from 'scule'
|
|
3
|
-
import {
|
|
3
|
+
import type { ContentNavigationItem } from '@nuxt/content'
|
|
4
4
|
|
|
5
5
|
definePageMeta({
|
|
6
6
|
layout: 'docs',
|
|
@@ -27,6 +27,30 @@ const { data: surround } = await useAsyncData(`${kebabCase(route.path)}-surround
|
|
|
27
27
|
})
|
|
28
28
|
})
|
|
29
29
|
|
|
30
|
+
const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
|
|
31
|
+
|
|
32
|
+
// console.log(JSON.stringify(navigation?.value, null, 2))
|
|
33
|
+
|
|
34
|
+
function makeBreadcrumb(items: ContentNavigationItem[], path: string, level = 0) {
|
|
35
|
+
const parent = [...items].find((i) => path.startsWith(i.path) && i.children?.length > 0)
|
|
36
|
+
if (!parent) {
|
|
37
|
+
return []
|
|
38
|
+
}
|
|
39
|
+
if (level === 0) {
|
|
40
|
+
return makeBreadcrumb(parent.children, path, level + 1)
|
|
41
|
+
}
|
|
42
|
+
return [
|
|
43
|
+
{
|
|
44
|
+
label: parent.title,
|
|
45
|
+
icon: parent.icon as string,
|
|
46
|
+
to: parent.page !== false ? parent.path : '',
|
|
47
|
+
},
|
|
48
|
+
...makeBreadcrumb(parent.children, path, level + 1),
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const breadcrumb = computed(() => makeBreadcrumb(navigation?.value || [], page.value.path))
|
|
53
|
+
|
|
30
54
|
usePageSEO({
|
|
31
55
|
title: `${page.value?.title} - ${appConfig.site.name}`,
|
|
32
56
|
ogTitle: page.value?.title,
|
|
@@ -36,12 +60,11 @@ usePageSEO({
|
|
|
36
60
|
|
|
37
61
|
<template>
|
|
38
62
|
<UPage v-if="page">
|
|
39
|
-
<UPageHeader
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
/>
|
|
63
|
+
<UPageHeader :title="page.title" :description="page.description" :links="page.links">
|
|
64
|
+
<template #headline>
|
|
65
|
+
<UBreadcrumb :items="breadcrumb" />
|
|
66
|
+
</template>
|
|
67
|
+
</UPageHeader>
|
|
45
68
|
|
|
46
69
|
<template #right>
|
|
47
70
|
<UContentToc title="On this page" :links="page.body?.toc?.links || []" highlight />
|