]> juplo.de Git - website/commitdiff
Breadcrumb spiegelt kanonischen Nav-Pfad für alle Seiten wider
authorKai Moritz <kai.milan.moritz@googlemail.com>
Sun, 21 Jun 2026 07:36:50 +0000 (07:36 +0000)
committerKai Moritz <kai.milan.moritz@googlemail.com>
Sun, 21 Jun 2026 07:36:50 +0000 (07:36 +0000)
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 <noreply@anthropic.com>
src/components/BlogNav.astro
src/lib/about.ts [new file with mode: 0644]
src/pages/about.html.astro
src/pages/agb.html.astro
src/pages/contact.html.astro
src/pages/datenschutz.html.astro
src/pages/google-analytics.html.astro
src/pages/haftung-inhalte.html.astro
src/pages/haftung-links.html.astro
src/pages/impressum.html.astro
src/pages/urheberrechte.html.astro

index 17893e30fac869b1e4784502fd49d6b8a87bcd0c..49882311be0d27c77156b0189f055eacfd74c837 100644 (file)
@@ -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 (file)
index 0000000..ef0e18b
--- /dev/null
@@ -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;
+}
index 8b9f125b1e9b989f18a0d33976ebf9f726a1fb8d..23920f6a5bc1aec3c660c5121b62b63c0ac2a740 100644 (file)
@@ -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 },
 ];
 ---
 
index 464534c5b0f1282f3d9ea9c853210a714b71d529..1253ef1873974c619021e5244f054d42e09247af 100644 (file)
@@ -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 },
 ];
 ---
 
index 0deb913f979b7b790024f9f9eb6009042eb9800f..7cc610dfdce57a17dbc11568444d0750eedd32fe 100644 (file)
@@ -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 },
 ];
 ---
 
index 9e404ce02899cb6e652dfbbe13bceafb8b0fb996..7ba67a1a503e442176411eb7e3797c9dfdce7b98 100644 (file)
@@ -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 },
 ];
 ---
 
index 5dd84f74c9f7aa065192a6a2e114b846f922a0fa..d505cd4351e6b8dc639f8e29877ff615d9bea014 100644 (file)
@@ -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 },
 ];
 ---
 
index f968bf08758a6c38abd5d21851b415970d465348..04bb17ec55b11452ff56f103a137c98c269ac525 100644 (file)
@@ -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 },
 ];
 ---
 
index 9e33cc17f5c509fbd0534ecefae3749f026c39eb..2b77c912f1d611085da923c8250bc8e2a9db6cc4 100644 (file)
@@ -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 },
 ];
 ---
 
index c762d42e971ed002ecdbd4a58bd255c6779454d2..f5a64a209b4f8c8bc1d275a94df919834977043c 100644 (file)
@@ -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 },
 ];
 ---
 
index a5a559202fa29c9c99512bba12863a751555e232..8158ad9310dd27edfcb2f27c3ce043d2bd68cc4a 100644 (file)
@@ -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 },
 ];
 ---