FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

docs: improve footer display, add pages, openapi.json + .md routes · unjs/unifont@ea2d1cb · GitHub

/ unifont Public

Commit ea2d1cb

Browse files
committed
docs: improve footer display, add pages, openapi.json + .md routes
1 parent e9042fe commit ea2d1cb

40 files changed

Lines changed: 1967 additions & 63 deletions

‎docs/app/app.vue‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,23 @@ if (import.meta.server) {
3737
})
3838
}
3939
40+
/** Prose routes with a markdown twin. */
41+
const PROSE = /^\/(?:$|fonts$|compare$|api$|about$|contact$|privacy$|docs)/
42+
43+
const canonical = computed(() => new URL(route.path, origin).href)
44+
45+
if (import.meta.server) {
46+
useHead({
47+
link: [{ rel: 'canonical', href: () => canonical.value }],
48+
})
49+
50+
// <https://acceptmarkdown.com>: the same document, negotiable through `Accept` or the suffix.
51+
if (PROSE.test(route.path)) {
52+
const markdown = route.path === '/' ? '/index.md' : `${route.path.replace(/\/$/, '')}.md`
53+
useHead({ link: [{ rel: 'alternate', type: 'text/markdown', href: new URL(markdown, origin).href }] })
54+
}
55+
}
56+
4057
const main = useTemplateRef<HTMLElement>('main')
4158
4259
// Reset focus to the main landmark after a client-side navigation, which otherwise leaves the

‎docs/app/components/SiteFooter.vue‎

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ const { data: credits } = await useFetch<ContributorsResponse>('/api/v1/contribu
99
})
1010
1111
const contributors = computed(() => credits.value?.contributors ?? [])
12-
// The API sorts by commit count, so the first entry leads.
13-
const top = computed(() => contributors.value[0]!)
12+
13+
/** Named above: danielroe, qwerzl, florian-lefebvre. Zero when GitHub is rate-limited. */
14+
const others = computed(() => Math.max(0, contributors.value.length - 3))
1415
</script>
1516

1617
<template>
@@ -24,47 +25,24 @@ const top = computed(() => contributors.value[0]!)
2425
<NuxtLink to="/compare">compare</NuxtLink> ·
2526
<NuxtLink to="/docs">docs</NuxtLink> ·
2627
<NuxtLink to="/api">api</NuxtLink> ·
28+
<NuxtLink to="/about">about</NuxtLink> ·
29+
<NuxtLink to="/contact">contact</NuxtLink> ·
30+
<NuxtLink to="/privacy">privacy</NuxtLink> ·
2731
<a href="https://github.com/unjs/unifont">source</a> ·
28-
<a href="https://npmjs.com/package/unifont">npm</a>
32+
<a href="https://npmjs.com/package/unifont">npm</a> ·
33+
<a href="/llms.txt">llms.txt</a> ·
34+
<a href="/openapi.json">openapi.json</a>
2935
</p>
30-
<div
31-
v-if="contributors.length"
32-
class="credits"
33-
>
34-
<!-- A link per avatar is a link and a list item per person once the styles are gone, so the
35-
row is decorative and the sentence below carries the same information as text. -->
36-
<div
37-
class="credits__row"
38-
aria-hidden="true"
39-
>
40-
<img
41-
v-for="person in contributors"
42-
:key="person.login"
43-
class="credits__avatar"
44-
:src="person.avatar"
45-
alt=""
46-
width="28"
47-
height="28"
48-
loading="lazy"
49-
decoding="async"
50-
>
51-
</div>
52-
<p class="credits__summary">
53-
{{ contributors.length }}
54-
{{ contributors.length === 1 ? 'person has' : 'people have' }}
55-
landed a commit, {{ top.login }} the most with {{ top.contributions }}
56-
{{ top.contributions === 1 ? 'commit' : 'commits' }}.
57-
<a href="https://github.com/unjs/unifont/graphs/contributors">all contributors</a>
58-
</p>
59-
</div>
6036

6137
<p class="colophon__line colophon__line--fine">
6238
made with <span
6339
class="heart"
6440
role="img"
6541
aria-label="love"
6642
>&hearts;</span> by
67-
<a href="https://roe.dev">Daniel Roe</a> and contributors ·
43+
<a href="https://roe.dev">danielroe</a>, <a href="https://github.com/qwerzl">qwerzl</a><template v-if="others">, </template><template v-else> and </template><a href="https://florian-lefebvre.dev/">florian-lefebvre</a><template v-if="others">
44+
and <a href="https://github.com/unjs/unifont/graphs/contributors">{{ others }} other contributor{{ others === 1 ? '' : 's' }}</a>
45+
</template> ·
6846
MIT · font data belongs to its foundries and is licensed by them, not by us
6947
</p>
7048
</div>

‎docs/app/components/SitePage.vue‎

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<script setup lang="ts">
2+
import type { MarkdownDocumentProps } from '@comark/vue'
3+
import { MarkdownDocument } from '@comark/vue'
4+
import { clientContent } from '~/composables/client-content'
5+
6+
const props = defineProps<{ slug: string }>()
7+
8+
const { data: page } = await useAsyncData(() => `page:${props.slug}`, () => clientContent.get(`/${props.slug}`))
9+
10+
if (!page.value) {
11+
throw createError({ statusCode: 404, statusMessage: 'No such page.' })
12+
}
13+
14+
const components = {
15+
pre: resolveComponent('ProsePre'),
16+
table: resolveComponent('ProseTable'),
17+
th: resolveComponent('ProseTh'),
18+
}
19+
20+
const title = computed(() => (page.value?.data.title as string | undefined) ?? 'unifont')
21+
const description = computed(() => page.value?.data.description as string | undefined)
22+
23+
// `ContentFile` carries the `nodes` the renderer wants, but the two types are declared in
24+
// different packages and do not structurally line up.
25+
const document = computed(() => page.value as MarkdownDocumentProps['value'])
26+
27+
usePageSeo({ title: () => title.value, description: () => description.value })
28+
</script>
29+
30+
<template>
31+
<article class="page">
32+
<header class="page__head">
33+
<h1 class="page__title">
34+
{{ title }}
35+
</h1>
36+
<p
37+
v-if="description"
38+
class="page__lede"
39+
>
40+
{{ description }}
41+
</p>
42+
</header>
43+
44+
<div class="page__body">
45+
<MarkdownDocument
46+
:value="document"
47+
:components="components"
48+
/>
49+
</div>
50+
</article>
51+
</template>
52+
53+
<style scoped>
54+
.page {
55+
max-width: 46rem;
56+
margin-inline: auto;
57+
padding: var(--space-xl) var(--page-pad) var(--space-2xl);
58+
}
59+
60+
.page__head {
61+
padding-bottom: var(--space-lg);
62+
border-bottom: var(--rule-heavy) solid var(--color-ink);
63+
}
64+
65+
.page__title {
66+
font-size: var(--text-2xl);
67+
}
68+
69+
.page__lede {
70+
max-width: var(--measure);
71+
margin-top: var(--space-xs);
72+
color: var(--color-muted);
73+
font-size: var(--text-md);
74+
line-height: 1.5;
75+
}
76+
77+
.page__body {
78+
padding-top: var(--space-lg);
79+
}
80+
81+
.page__body :deep(p),
82+
.page__body :deep(ul),
83+
.page__body :deep(ol) {
84+
margin-block: var(--space-md);
85+
color: var(--color-muted);
86+
}
87+
88+
.page__body :deep(h2) {
89+
margin-block: var(--space-2xl) var(--space-sm);
90+
padding-top: var(--space-sm);
91+
border-top: var(--rule-hair) solid var(--color-rule);
92+
font-size: var(--text-lg);
93+
}
94+
95+
.page__body :deep(h3) {
96+
margin-block: var(--space-xl) var(--space-xs);
97+
font-size: var(--text-md);
98+
}
99+
100+
.page__body :deep(ul),
101+
.page__body :deep(ol) {
102+
padding-left: var(--space-lg);
103+
}
104+
105+
.page__body :deep(li) {
106+
margin-block: var(--space-2xs);
107+
}
108+
109+
.page__body :deep(li::marker) {
110+
color: var(--color-rule-strong);
111+
}
112+
113+
.page__body :deep(a) {
114+
text-decoration-color: var(--color-muted);
115+
}
116+
117+
.page__body :deep(code) {
118+
padding: 0.1em 0.25em;
119+
background: var(--color-paper-3);
120+
border-radius: var(--radius-xs);
121+
}
122+
</style>

‎docs/app/pages/about.vue‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<template>
2+
<SitePage slug="about" />
3+
</template>

‎docs/app/pages/api.vue‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@ const anchor = (path: string) => path.replace(/[^a-z0-9]+/gi, '-').replace(/^-|-
135135
plugins or CI checks, anywhere you want font metadata without shipping a resolver. There's an
136136
<a href="https://modelcontextprotocol.io">MCP</a> server too, if the thing asking is a model.
137137
</p>
138+
<p class="head__machine">
139+
Machine-readable: <a href="/openapi.json">openapi.json</a> describes every endpoint below,
140+
<a href="/llms.txt">llms.txt</a> indexes the site for agents, and every prose page here answers in
141+
markdown if you ask for <code>text/markdown</code> or append <code>.md</code> to its path.
142+
</p>
138143
<p class="head__warn">
139144
Not for production use. This API is best-effort, we'll rate-limit it if we have to, and it can
140145
change or go away, the same as <code>proxy.unifont.dev</code>. Build it into a script, an editor
@@ -283,6 +288,13 @@ const anchor = (path: string) => path.replace(/[^a-z0-9]+/gi, '-').replace(/^-|-
283288
color: var(--color-muted);
284289
}
285290
291+
.head__machine {
292+
max-width: var(--measure);
293+
margin-top: var(--space-sm);
294+
color: var(--color-muted);
295+
font-size: var(--text-sm);
296+
}
297+
286298
.head__warn {
287299
max-width: var(--measure);
288300
margin-top: var(--space-md);

‎docs/app/pages/contact.vue‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<template>
2+
<SitePage slug="contact" />
3+
</template>

‎docs/app/pages/docs/[[slug]].vue‎

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const route = useRoute()
88
const path = computed(() => {
99
const slug = route.params.slug
1010
const parts = Array.isArray(slug) ? slug : slug ? [slug] : []
11-
return parts.length ? `/${parts.join('/')}` : '/'
11+
return parts.length ? `/docs/${parts.join('/')}` : '/docs'
1212
})
1313
1414
const { data: page } = await useAsyncData(() => `doc:${path.value}`, () => clientContent.get(path.value), {
@@ -23,21 +23,25 @@ const { data: nav } = await useAsyncData('doc-nav', () => clientContent.navigati
2323
2424
interface NavItem { title?: string, path: string, children?: NavItem[] }
2525
26+
/**
27+
* The `/docs` subtree of the navigation, flattened into reading order. A directory and its own
28+
* index are separate nodes with the same path, so the first of each wins.
29+
*/
2630
const chapters = computed<NavItem[]>(() => {
2731
const items = (nav.value ?? []) as NavItem[]
28-
const flat: NavItem[] = []
32+
const flat = new Map<string, NavItem>()
2933
const walk = (list: NavItem[]) => {
3034
for (const item of list) {
31-
if (item.path) {
32-
flat.push(item)
35+
if ((item.path === '/docs' || item.path?.startsWith('/docs/')) && !flat.has(item.path)) {
36+
flat.set(item.path, item)
3337
}
3438
if (item.children?.length) {
3539
walk(item.children)
3640
}
3741
}
3842
}
3943
walk(items)
40-
return flat
44+
return [...flat.values()]
4145
})
4246
4347
const position = computed(() => chapters.value.findIndex(item => item.path === path.value))
@@ -64,8 +68,6 @@ usePageSeo({
6468
title: () => title.value,
6569
description: () => (page.value?.data.description as string | undefined) ?? undefined,
6670
})
67-
68-
const href = (docPath: string) => (docPath === '/' ? '/docs' : `/docs${docPath}`)
6971
</script>
7072

7173
<template>
@@ -86,7 +88,7 @@ const href = (docPath: string) => (docPath === '/' ? '/docs' : `/docs${docPath}`
8688
class="rail__link"
8789
:class="{ 'rail__link--current': item.path === path }"
8890
:aria-current="item.path === path ? 'page' : undefined"
89-
:to="href(item.path)"
91+
:to="item.path"
9092
>{{ item.title }}</NuxtLink>
9193
</li>
9294
</ol>
@@ -119,15 +121,15 @@ const href = (docPath: string) => (docPath === '/' ? '/docs' : `/docs${docPath}`
119121
<NuxtLink
120122
v-if="previous"
121123
class="pager__link"
122-
:to="href(previous.path)"
124+
:to="previous.path"
123125
>
124126
<span class="pager__dir">previous</span>
125127
<span class="pager__name">{{ previous.title }}</span>
126128
</NuxtLink>
127129
<NuxtLink
128130
v-if="next"
129131
class="pager__link pager__link--next"
130-
:to="href(next.path)"
132+
:to="next.path"
131133
>
132134
<span class="pager__dir">next</span>
133135
<span class="pager__name">{{ next.title }}</span>

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL