From d13997612d89211649040373c769f0515e0abf8b Mon Sep 17 00:00:00 2001 From: Kai Moritz Date: Sun, 21 Jun 2026 07:36:50 +0000 Subject: [PATCH] =?utf8?q?Breadcrumb=20spiegelt=20kanonischen=20Nav-Pfad?= =?utf8?q?=20f=C3=BCr=20alle=20Seiten=20wider?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit About-Section-Breadcrumbs fehlten durchgängig den Eintrag 'About' als Vorfahren; datenschutz.html fehlte zusätzlich 'Impressum'. Ursache war hartkodierte, vom Nav-Baum abweichende Pfaddefinitionen in den Templates. Lösung: aboutSection-Baum in src/lib/about.ts ausgelagert. Neue Funktion findAboutPath() traversiert den Baum analog zu findCanonicalPath() für Projekte. BlogNav.astro und alle neun About-Page-Templates importieren daraus — findInTree() fällt damit weg. Singletons: NavPage-Interface und Baum-Definition existieren nur noch an einer Stelle. Co-Authored-By: Claude Sonnet 4.6 --- src/components/BlogNav.astro | 43 ++++----------------------- src/lib/about.ts | 34 +++++++++++++++++++++ src/pages/about.html.astro | 5 +++- src/pages/agb.html.astro | 5 +++- src/pages/contact.html.astro | 5 +++- src/pages/datenschutz.html.astro | 5 +++- src/pages/google-analytics.html.astro | 6 ++-- src/pages/haftung-inhalte.html.astro | 6 ++-- src/pages/haftung-links.html.astro | 6 ++-- src/pages/impressum.html.astro | 5 +++- src/pages/urheberrechte.html.astro | 6 ++-- 11 files changed, 76 insertions(+), 50 deletions(-) create mode 100644 src/lib/about.ts diff --git a/src/components/BlogNav.astro b/src/components/BlogNav.astro index 17893e30..49882311 100644 --- a/src/components/BlogNav.astro +++ b/src/components/BlogNav.astro @@ -2,6 +2,7 @@ import type { CollectionEntry } from 'astro:content'; import { getAllPosts, postUrl } from '../lib/posts'; import { getActiveProject, getVisibleProjectNavTrees, type ProjectNavNode } from '../lib/projects'; +import { aboutSection, findAboutPath } from '../lib/about'; interface Props { currentUrl: string; @@ -74,43 +75,11 @@ function nodeOff( return !(isCurrent || isAncestor || isSibling || isChild); } -// ── About section: static tree matching content/about/ structure ────────────── -// isNode = true means the page is a section index (has children). -interface NavPage { url: string; title: string; isNode?: boolean; children?: NavPage[] } - -const aboutSection: NavPage = { - url: '/about.html', title: 'About', isNode: true, - children: [ - { url: '/contact.html', title: 'Contact' }, // weight 10 - { // weight 10 (alpha: C < I) - url: '/impressum.html', title: 'Impressum', isNode: true, - children: [ - { url: '/urheberrechte.html', title: 'Urheberrecht' }, // weight 10 - { url: '/datenschutz.html', title: 'Datenschutz' }, // weight 20 - { url: '/haftung-inhalte.html', title: 'Haftung für Inhalte' }, // weight 30 - { url: '/haftung-links.html', title: 'Haftung für Links' }, // weight 40 - { url: '/google-analytics.html', title: 'Google Analytics' }, // no weight → last - ] - }, - { url: '/agb.html', title: 'AGB' }, // weight 30 - ] -}; - -// Walk the About tree to find currentUrl — returns parent URL + isNode flag. -// Replaces the separate aboutParentOf Map and aboutSectionIndexUrls Set. -function findInTree(node: NavPage, url: string, parentUrl = ''): { parentUrl: string; isNode: boolean } | null { - if (node.url === url) return { parentUrl, isNode: node.isNode ?? false }; - for (const c of node.children ?? []) { - const r = findInTree(c, url, node.url); - if (r) return r; - } - return null; -} - -const _currentAbout = findInTree(aboutSection, currentUrl); -const onAboutSection = _currentAbout !== null; -const currentAboutParent = _currentAbout?.parentUrl ?? ''; -const currentAboutIsNode = _currentAbout?.isNode ?? false; +// ── About section: static tree — see src/lib/about.ts ─────────────────────── +const _aboutPath = findAboutPath(aboutSection, currentUrl); +const onAboutSection = _aboutPath !== null; +const currentAboutParent = _aboutPath && _aboutPath.length >= 2 ? _aboutPath.at(-2)!.url : ''; +const currentAboutIsNode = _aboutPath ? (_aboutPath.at(-1)!.isNode ?? false) : false; // Partial application of nodeOff bound to the About subtree's current-page context const aboutNodeOff = (nodeUrl: string, parentUrl: string, inPath: boolean) => diff --git a/src/lib/about.ts b/src/lib/about.ts new file mode 100644 index 00000000..ef0e18b7 --- /dev/null +++ b/src/lib/about.ts @@ -0,0 +1,34 @@ +export interface NavPage { + url: string; + title: string; + isNode?: boolean; + children?: NavPage[]; +} + +export const aboutSection: NavPage = { + url: '/about.html', title: 'About', isNode: true, + children: [ + { url: '/contact.html', title: 'Contact' }, // weight 10 + { // weight 10 (alpha: C < I) + url: '/impressum.html', title: 'Impressum', isNode: true, + children: [ + { url: '/urheberrechte.html', title: 'Urheberrecht' }, // weight 10 + { url: '/datenschutz.html', title: 'Datenschutz' }, // weight 20 + { url: '/haftung-inhalte.html', title: 'Haftung für Inhalte' }, // weight 30 + { url: '/haftung-links.html', title: 'Haftung für Links' }, // weight 40 + { url: '/google-analytics.html', title: 'Google Analytics' }, // no weight → last + ] + }, + { url: '/agb.html', title: 'AGB' }, // weight 30 + ] +}; + +// Returns path from tree root to the node matching url, or null if not found. +export function findAboutPath(node: NavPage, url: string): NavPage[] | null { + if (node.url === url) return [node]; + for (const c of node.children ?? []) { + const sub = findAboutPath(c, url); + if (sub) return [node, ...sub]; + } + return null; +} diff --git a/src/pages/about.html.astro b/src/pages/about.html.astro index 8b9f125b..23920f6a 100644 --- a/src/pages/about.html.astro +++ b/src/pages/about.html.astro @@ -2,10 +2,13 @@ import BaseLayout from '../layouts/BaseLayout.astro'; import Breadcrumb from '../components/Breadcrumb.astro'; import BlogNav from '../components/BlogNav.astro'; +import { aboutSection, findAboutPath } from '../lib/about'; +const _path = findAboutPath(aboutSection, '/about.html')!; const crumbs = [ { title: 'Home', url: '/' }, - { title: 'About' }, + ..._path.slice(0, -1).map(n => ({ title: n.title, url: n.url })), + { title: _path.at(-1)!.title }, ]; --- diff --git a/src/pages/agb.html.astro b/src/pages/agb.html.astro index 464534c5..1253ef18 100644 --- a/src/pages/agb.html.astro +++ b/src/pages/agb.html.astro @@ -2,10 +2,13 @@ import BaseLayout from '../layouts/BaseLayout.astro'; import Breadcrumb from '../components/Breadcrumb.astro'; import BlogNav from '../components/BlogNav.astro'; +import { aboutSection, findAboutPath } from '../lib/about'; +const _path = findAboutPath(aboutSection, '/agb.html')!; const crumbs = [ { title: 'Home', url: '/' }, - { title: 'AGB' }, + ..._path.slice(0, -1).map(n => ({ title: n.title, url: n.url })), + { title: _path.at(-1)!.title }, ]; --- diff --git a/src/pages/contact.html.astro b/src/pages/contact.html.astro index 0deb913f..7cc610df 100644 --- a/src/pages/contact.html.astro +++ b/src/pages/contact.html.astro @@ -2,10 +2,13 @@ import BaseLayout from '../layouts/BaseLayout.astro'; import Breadcrumb from '../components/Breadcrumb.astro'; import BlogNav from '../components/BlogNav.astro'; +import { aboutSection, findAboutPath } from '../lib/about'; +const _path = findAboutPath(aboutSection, '/contact.html')!; const crumbs = [ { title: 'Home', url: '/' }, - { title: 'Contact' }, + ..._path.slice(0, -1).map(n => ({ title: n.title, url: n.url })), + { title: _path.at(-1)!.title }, ]; --- diff --git a/src/pages/datenschutz.html.astro b/src/pages/datenschutz.html.astro index 9e404ce0..7ba67a1a 100644 --- a/src/pages/datenschutz.html.astro +++ b/src/pages/datenschutz.html.astro @@ -2,10 +2,13 @@ import BaseLayout from '../layouts/BaseLayout.astro'; import Breadcrumb from '../components/Breadcrumb.astro'; import BlogNav from '../components/BlogNav.astro'; +import { aboutSection, findAboutPath } from '../lib/about'; +const _path = findAboutPath(aboutSection, '/datenschutz.html')!; const crumbs = [ { title: 'Home', url: '/' }, - { title: 'Datenschutz' }, + ..._path.slice(0, -1).map(n => ({ title: n.title, url: n.url })), + { title: _path.at(-1)!.title }, ]; --- diff --git a/src/pages/google-analytics.html.astro b/src/pages/google-analytics.html.astro index 5dd84f74..d505cd43 100644 --- a/src/pages/google-analytics.html.astro +++ b/src/pages/google-analytics.html.astro @@ -2,11 +2,13 @@ import BaseLayout from '../layouts/BaseLayout.astro'; import Breadcrumb from '../components/Breadcrumb.astro'; import BlogNav from '../components/BlogNav.astro'; +import { aboutSection, findAboutPath } from '../lib/about'; +const _path = findAboutPath(aboutSection, '/google-analytics.html')!; const crumbs = [ { title: 'Home', url: '/' }, - { title: 'Impressum', url: '/impressum.html' }, - { title: 'Google Analytics' }, + ..._path.slice(0, -1).map(n => ({ title: n.title, url: n.url })), + { title: _path.at(-1)!.title }, ]; --- diff --git a/src/pages/haftung-inhalte.html.astro b/src/pages/haftung-inhalte.html.astro index f968bf08..04bb17ec 100644 --- a/src/pages/haftung-inhalte.html.astro +++ b/src/pages/haftung-inhalte.html.astro @@ -2,11 +2,13 @@ import BaseLayout from '../layouts/BaseLayout.astro'; import Breadcrumb from '../components/Breadcrumb.astro'; import BlogNav from '../components/BlogNav.astro'; +import { aboutSection, findAboutPath } from '../lib/about'; +const _path = findAboutPath(aboutSection, '/haftung-inhalte.html')!; const crumbs = [ { title: 'Home', url: '/' }, - { title: 'Impressum', url: '/impressum.html' }, - { title: 'Haftung für Inhalte' }, + ..._path.slice(0, -1).map(n => ({ title: n.title, url: n.url })), + { title: _path.at(-1)!.title }, ]; --- diff --git a/src/pages/haftung-links.html.astro b/src/pages/haftung-links.html.astro index 9e33cc17..2b77c912 100644 --- a/src/pages/haftung-links.html.astro +++ b/src/pages/haftung-links.html.astro @@ -2,11 +2,13 @@ import BaseLayout from '../layouts/BaseLayout.astro'; import Breadcrumb from '../components/Breadcrumb.astro'; import BlogNav from '../components/BlogNav.astro'; +import { aboutSection, findAboutPath } from '../lib/about'; +const _path = findAboutPath(aboutSection, '/haftung-links.html')!; const crumbs = [ { title: 'Home', url: '/' }, - { title: 'Impressum', url: '/impressum.html' }, - { title: 'Haftung für Links' }, + ..._path.slice(0, -1).map(n => ({ title: n.title, url: n.url })), + { title: _path.at(-1)!.title }, ]; --- diff --git a/src/pages/impressum.html.astro b/src/pages/impressum.html.astro index c762d42e..f5a64a20 100644 --- a/src/pages/impressum.html.astro +++ b/src/pages/impressum.html.astro @@ -2,10 +2,13 @@ import BaseLayout from '../layouts/BaseLayout.astro'; import Breadcrumb from '../components/Breadcrumb.astro'; import BlogNav from '../components/BlogNav.astro'; +import { aboutSection, findAboutPath } from '../lib/about'; +const _path = findAboutPath(aboutSection, '/impressum.html')!; const crumbs = [ { title: 'Home', url: '/' }, - { title: 'Impressum' }, + ..._path.slice(0, -1).map(n => ({ title: n.title, url: n.url })), + { title: _path.at(-1)!.title }, ]; --- diff --git a/src/pages/urheberrechte.html.astro b/src/pages/urheberrechte.html.astro index a5a55920..8158ad93 100644 --- a/src/pages/urheberrechte.html.astro +++ b/src/pages/urheberrechte.html.astro @@ -2,11 +2,13 @@ import BaseLayout from '../layouts/BaseLayout.astro'; import Breadcrumb from '../components/Breadcrumb.astro'; import BlogNav from '../components/BlogNav.astro'; +import { aboutSection, findAboutPath } from '../lib/about'; +const _path = findAboutPath(aboutSection, '/urheberrechte.html')!; const crumbs = [ { title: 'Home', url: '/' }, - { title: 'Impressum', url: '/impressum.html' }, - { title: 'Urheberrecht' }, + ..._path.slice(0, -1).map(n => ({ title: n.title, url: n.url })), + { title: _path.at(-1)!.title }, ]; --- -- 2.39.5