Skip to content

Commit 760c97a

Browse files
committed
Unify release notes with docs navigation
1 parent 41aaaa2 commit 760c97a

7 files changed

Lines changed: 313 additions & 93 deletions

File tree

docs/.vitepress/config.mts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -275,17 +275,10 @@ export default withMermaid(defineConfig({
275275
{ text: 'OIDC Integration Guide', link: '/oidc-integration-guide/introduction' },
276276
{ text: 'RADIUS Gateway Guide', link: '/radius-interface-gateway-guide/introduction' },
277277
{ text: 'Release Notes', link: '/release-notes/' },
278-
{
279-
text: 'Legal',
280-
items: [
281-
{ text: 'Imprint', link: '/legal/imprint' },
282-
{ text: 'Privacy Notice', link: '/legal/privacy' }
283-
]
284-
}
285278
],
286279

287280
sidebar: {
288-
'/release-notes/': false,
281+
'/release-notes/': [],
289282
'/rest-api-guide/': [
290283
{
291284
text: 'REST API Guide',

docs/.vitepress/theme/ReleaseNotesLayout.vue

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ function formatDate(dateStr) {
1515

1616
<template>
1717
<div class="blog-layout">
18-
<div class="blog-hero">
19-
<h1 style="color: #fff; margin: 0; font-size: 2em;">Release Notes</h1>
20-
<p style="opacity: 0.85; margin-top: 8px; font-size: 1em;">
21-
New features and updates for Mobile ID
22-
</p>
18+
<div class="blog-hero blog-hero--centered">
19+
<div class="blog-hero-copy">
20+
<h1 style="color: #fff; margin: 0; font-size: 2em;">Release Notes</h1>
21+
<p style="opacity: 0.85; margin: 10px 0 0; font-size: 1em;">
22+
New features and updates for Mobile ID
23+
</p>
24+
</div>
2325
</div>
2426

2527
<div style="padding: 32px 24px; max-width: 800px; margin: 0 auto;">

docs/.vitepress/theme/ReleaseNotesPostLayout.vue

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,46 @@
11
<script setup>
22
import { useData } from 'vitepress'
3+
import { computed } from 'vue'
34
import LegalFooter from './components/LegalFooter.vue'
5+
import ReleaseNotesSidebar from './components/ReleaseNotesSidebar.vue'
46
5-
const { frontmatter } = useData()
7+
const { frontmatter, page } = useData()
8+
9+
const META_COPY = {
10+
en: {
11+
section: 'Release Notes',
12+
readingTime: 'min read',
13+
locale: 'en-US',
14+
},
15+
de: {
16+
section: 'Release Notes',
17+
readingTime: 'Min. Lesezeit',
18+
locale: 'de-CH',
19+
},
20+
fr: {
21+
section: 'Release Notes',
22+
readingTime: 'min de lecture',
23+
locale: 'fr-CH',
24+
},
25+
it: {
26+
section: 'Release Notes',
27+
readingTime: 'min di lettura',
28+
locale: 'it-CH',
29+
},
30+
}
31+
32+
const currentLang = computed(() => {
33+
const rel = page.value.relativePath || ''
34+
const langFromPath = rel.match(/\.(de|fr|it)\.md$/)?.[1]
35+
return langFromPath || frontmatter.value.lang || 'en'
36+
})
37+
38+
const metaCopy = computed(() => META_COPY[currentLang.value] ?? META_COPY.en)
639
740
function formatDate(dateStr) {
841
if (!dateStr) return ''
942
const d = new Date(dateStr)
10-
return d.toLocaleDateString('en-US', {
43+
return d.toLocaleDateString(metaCopy.value.locale, {
1144
year: 'numeric',
1245
month: 'long',
1346
day: 'numeric',
@@ -20,22 +53,26 @@ function formatDate(dateStr) {
2053
<!-- Hero Header -->
2154
<div class="blog-hero">
2255
<div style="max-width: 800px; margin: 0 auto;">
23-
<div style="font-size: 0.75em; letter-spacing: 1px; text-transform: uppercase; opacity: 0.7; margin-bottom: 12px; font-family: 'Lato', sans-serif;">
24-
Release Notes
56+
<div style="font-size: 0.75em; letter-spacing: 1px; text-transform: uppercase; opacity: 0.7; margin-bottom: 12px; font-family: var(--vp-font-family-base);">
57+
{{ metaCopy.section }}
2558
</div>
2659
<h1 style="color: #fff; margin: 0 0 16px; font-size: 2em; line-height: 1.2;">
2760
{{ frontmatter.title }}
2861
</h1>
29-
<div style="display: flex; gap: 20px; font-size: 0.85em; opacity: 0.85; font-family: 'Lato', sans-serif; flex-wrap: wrap;">
62+
<div style="display: flex; gap: 20px; font-size: 0.85em; opacity: 0.85; font-family: var(--vp-font-family-base); flex-wrap: wrap;">
3063
<span v-if="frontmatter.date">📅 {{ formatDate(frontmatter.date) }}</span>
31-
<span v-if="frontmatter.readingTime">⏱ {{ frontmatter.readingTime }} min read</span>
64+
<span v-if="frontmatter.readingTime">⏱ {{ frontmatter.readingTime }} {{ metaCopy.readingTime }}</span>
3265
</div>
3366
</div>
3467
</div>
3568
36-
<!-- Content rendered from Markdown -->
37-
<div class="blog-content">
38-
<Content />
69+
<div class="blog-post-shell">
70+
<ReleaseNotesSidebar />
71+
72+
<!-- Content rendered from Markdown -->
73+
<div class="blog-content blog-content--post">
74+
<Content />
75+
</div>
3976
</div>
4077
4178
<LegalFooter />
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<script setup>
2+
import { useData } from 'vitepress'
3+
import { computed } from 'vue'
4+
import { data as posts } from '../release-notes.sidebar.data.mts'
5+
6+
const { frontmatter, page } = useData()
7+
8+
const COPY = {
9+
en: {
10+
group: 'Release Notes',
11+
overview: 'Overview',
12+
},
13+
de: {
14+
group: 'Release Notes',
15+
overview: 'Übersicht',
16+
},
17+
fr: {
18+
group: 'Release Notes',
19+
overview: 'Aperçu',
20+
},
21+
it: {
22+
group: 'Release Notes',
23+
overview: 'Panoramica',
24+
},
25+
}
26+
27+
const currentLang = computed(() => {
28+
const rel = page.value.relativePath || ''
29+
const langFromPath = rel.match(/\.(de|fr|it)\.md$/)?.[1]
30+
return langFromPath || frontmatter.value.lang || 'en'
31+
})
32+
33+
const currentUrl = computed(() => `/${(page.value.relativePath || '').replace(/\.md$/, '.html')}`)
34+
35+
const labels = computed(() => COPY[currentLang.value] ?? COPY.en)
36+
37+
const sidebarPosts = computed(() => {
38+
const variantsByBaseUrl = new Map()
39+
40+
for (const post of posts) {
41+
const variants = variantsByBaseUrl.get(post.baseUrl) ?? new Map()
42+
variants.set(post.lang, post)
43+
variantsByBaseUrl.set(post.baseUrl, variants)
44+
}
45+
46+
return Array.from(variantsByBaseUrl.values())
47+
.map((variants) => (
48+
variants.get(currentLang.value)
49+
?? variants.get('en')
50+
?? Array.from(variants.values())[0]
51+
))
52+
.sort((a, b) => +new Date(b.date) - +new Date(a.date))
53+
})
54+
</script>
55+
56+
<template>
57+
<aside class="blog-sidebar" aria-label="Release notes navigation">
58+
<div class="blog-sidebar-group-title">{{ labels.group }}</div>
59+
<nav class="blog-sidebar-nav">
60+
<a
61+
href="/release-notes/"
62+
class="blog-sidebar-link"
63+
:class="{ 'is-active': currentUrl === '/release-notes/' }"
64+
:aria-current="currentUrl === '/release-notes/' ? 'page' : undefined"
65+
>
66+
{{ labels.overview }}
67+
</a>
68+
<a
69+
v-for="post in sidebarPosts"
70+
:key="post.url"
71+
:href="post.url"
72+
class="blog-sidebar-link"
73+
:class="{ 'is-active': post.url === currentUrl }"
74+
:aria-current="post.url === currentUrl ? 'page' : undefined"
75+
>
76+
{{ post.title }}
77+
</a>
78+
</nav>
79+
</aside>
80+
</template>

docs/.vitepress/theme/index.js

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import DefaultTheme from 'vitepress/theme'
22
import mediumZoom from 'medium-zoom'
33
import { h, onMounted, watch, nextTick } from 'vue'
4-
import { useRoute, useData } from 'vitepress'
4+
import { useRoute } from 'vitepress'
55
import { theme as openApiTheme, useOpenapi } from 'vitepress-openapi/client'
66
import 'vitepress-openapi/dist/style.css'
77
import specYaml from '../../public/openapi-mobileid.yaml?raw'
@@ -15,20 +15,15 @@ export default {
1515
extends: DefaultTheme,
1616
Layout() {
1717
return h({
18-
setup() {
19-
const { frontmatter } = useData()
20-
return () => {
21-
const layout = frontmatter.value.layout
22-
if (layout === 'release-notes-index') return h(ReleaseNotesLayout)
23-
if (layout === 'release-notes-post') return h(ReleaseNotesPostLayout)
24-
return h(DefaultTheme.Layout, null, {
25-
'doc-after': () => h(DocFeedback),
26-
})
27-
}
28-
}
18+
setup: () => () => h(DefaultTheme.Layout, null, {
19+
'doc-after': () => h(DocFeedback),
20+
}),
2921
})
3022
},
3123
async enhanceApp({ app }) {
24+
app.component('release-notes-index', ReleaseNotesLayout)
25+
app.component('release-notes-post', ReleaseNotesPostLayout)
26+
3227
useOpenapi({
3328
spec: specYaml,
3429
config: {

0 commit comments

Comments
 (0)