]> juplo.de Git - website/commitdiff
Projects: Routing-Pages, ProjectPage-Komponente und BlogNav-Integration
authorKai Moritz <kai.milan.moritz@googlemail.com>
Sun, 14 Jun 2026 11:59:59 +0000 (11:59 +0000)
committerKai Moritz <kai.milan.moritz@googlemail.com>
Sun, 14 Jun 2026 11:59:59 +0000 (11:59 +0000)
- ProjectPage.astro: wiederverwendbare Komponente für alle Projektseiten
  (liest Collection-Entry per ID, rendert HTML-Body via set:html, setzt canonical)
- 12 statische Routing-Dateien: 6 für 2.1.1 (visible) unter /hibernate-maven-plugin/,
  6 für 2.1.2-SNAPSHOT unter /projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/
- BlogNav: Projects-Ast zwischen Blog und About eingefügt mit vollständiger
  off-Logik (tree.html-Analogon); SNAPSHOT-Seiten zeigen SNAPSHOT-NavTree
  an der Position der sichtbaren Version; off-Logic für Blog/About angepasst

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
15 files changed:
src/components/BlogNav.astro
src/components/ProjectPage.astro [new file with mode: 0644]
src/pages/hibernate-maven-plugin/configuration.html.astro [new file with mode: 0644]
src/pages/hibernate-maven-plugin/create-mojo.html.astro [new file with mode: 0644]
src/pages/hibernate-maven-plugin/index.astro [new file with mode: 0644]
src/pages/hibernate-maven-plugin/plugin-info.html.astro [new file with mode: 0644]
src/pages/hibernate-maven-plugin/project-info.html.astro [new file with mode: 0644]
src/pages/hibernate-maven-plugin/project-reports.html.astro [new file with mode: 0644]
src/pages/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/configuration.html.astro [new file with mode: 0644]
src/pages/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/create-mojo.html.astro [new file with mode: 0644]
src/pages/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/index.astro [new file with mode: 0644]
src/pages/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/plugin-info.html.astro [new file with mode: 0644]
src/pages/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/project-info.html.astro [new file with mode: 0644]
src/pages/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/project-reports.html.astro [new file with mode: 0644]
src/pages/projects/index.astro [new file with mode: 0644]

index c489209cb83a3f1f6ab3ab39911b157081b24d9d..5c05a463b879a00c7dbe0fed59e621a05fba80a9 100644 (file)
@@ -1,6 +1,7 @@
 ---
 import type { CollectionEntry } from 'astro:content';
 import { getAllPosts, postUrl } from '../lib/posts';
+import { getActiveProject, getVisibleProjectNavTrees, type ProjectNavNode } from '../lib/projects';
 
 interface Props {
   currentUrl: string;
@@ -105,6 +106,59 @@ function aboutPageOff(page: NavPage, parentUrl: string): boolean {
   return !(isCurrent || isAncestor || isSibling || isChild);
 }
 
+// ── Projects section ──────────────────────────────────────────────────────────
+const activeProject      = await getActiveProject(currentUrl);
+const onProjectsSection  = currentUrl === '/projects/' || activeProject !== null;
+// When on a SNAPSHOT page, render that version's tree; otherwise all visible trees.
+const projectTrees = onProjectsSection && activeProject && !activeProject.isCurrent
+  ? [activeProject]
+  : await getVisibleProjectNavTrees();
+
+// Nav helpers for project trees
+function inProjectSubtree(node: ProjectNavNode, url: string): boolean {
+  if (node.url === url) return true;
+  return node.children.some(c => inProjectSubtree(c, url));
+}
+
+// Find the parent node of a URL within a tree
+function findProjectParent(node: ProjectNavNode, url: string): ProjectNavNode | null {
+  for (const c of node.children) {
+    if (c.url === url) return node;
+    const found = findProjectParent(c, url);
+    if (found) return found;
+  }
+  return null;
+}
+
+// Find a node by URL
+function findProjectNode(node: ProjectNavNode, url: string): ProjectNavNode | null {
+  if (node.url === url) return node;
+  for (const c of node.children) {
+    const found = findProjectNode(c, url);
+    if (found) return found;
+  }
+  return null;
+}
+
+// Determine the "active" project URL — either from visible page or SNAPSHOT page
+const activeProjectUrl = activeProject?.navRoot.url ?? null;
+// The active project's current node and its parent (for off-logic)
+const _activeProjectRoot = activeProject?.navRoot ?? null;
+const activeProjectCurrentNode = _activeProjectRoot ? findProjectNode(_activeProjectRoot, currentUrl) : null;
+const activeProjectCurrentParent = _activeProjectRoot ? findProjectParent(_activeProjectRoot, currentUrl) : null;
+const activeProjectCurrentIsNode = activeProjectCurrentNode?.isNode ?? false;
+
+// off-logic mirroring tree.html for a node within a project tree
+function projectNodeOff(node: ProjectNavNode, parentUrl: string): boolean {
+  const isCurrent  = node.url === currentUrl;
+  const isAncestor = !isCurrent && inProjectSubtree(node, currentUrl);
+  // Sibling: same parent as current page, and current is NOT a section node
+  const isSibling  = parentUrl === (activeProjectCurrentParent?.url ?? '') && !activeProjectCurrentIsNode;
+  // Child: this node's parent is the current page
+  const isChild    = parentUrl === currentUrl;
+  return !(isCurrent || isAncestor || isSibling || isChild);
+}
+
 // ── Blog URL classification ────────────────────────────────────────────────────
 const mainSections = [
   { name: 'blog',     url: '/blog/',      title: 'Blog' },
@@ -131,16 +185,16 @@ const tagsOff       = !blogSelected || onArchive      || onCategoryTerm;
 
 // Active section for #menu highlight
 const activeSection = mainSections.find(s => {
-  if (s.name === 'blog')  return blogSelected;
-  if (s.name === 'about') return onAboutSection;
+  if (s.name === 'blog')     return blogSelected;
+  if (s.name === 'about')    return onAboutSection;
+  if (s.name === 'projects') return onProjectsSection;
   return currentUrl.startsWith(s.url);
 })?.name ?? '';
 
-// Top-level section off-logic (Hugo blog.html vs default.html + tree.html):
-// Blog section hidden when viewing any About-section page (and later: Projects pages).
-// About section hidden only when on Blog pages.
-const blogSectionOff = onAboutSection;
-const aboutOff       = blogSelected;
+// Top-level section off-logic
+const blogSectionOff     = onAboutSection || onProjectsSection;
+const projectsSectionOff = blogSelected   || onAboutSection;
+const aboutOff           = blogSelected   || onProjectsSection;
 
 // Active year / taxonomy term helpers
 function isActiveYear(year: string) {
@@ -271,6 +325,72 @@ const activeTagSlug  = onTagTerm      ? currentUrl.replace(/^\/tags\//, '').repl
       </ul>
     </li>
 
+    {/* ── Projects subtree ─────────────────────────────────────────────────── */}
+    <li class={`s sub${projectsSectionOff ? ' off' : ''}`}>
+      <a href="/projects/" class={onProjectsSection ? 's selected' : 's'}>
+        {onProjectsSection ? <strong>Projects</strong> : 'Projects'}
+      </a>
+      <ul class={`s${onProjectsSection ? ' active' : ''}`}>
+        {projectTrees.map(info => {
+          const root = info.navRoot;
+          const rootOff = projectNodeOff(root, '/projects/');
+          const rootInPath = inProjectSubtree(root, currentUrl);
+          return (
+            <li class={`s sub${rootOff ? ' off' : ''}`}>
+              <a href={root.url} class={rootInPath ? 's selected' : 's'}>
+                {rootInPath ? <strong>{root.title}</strong> : root.title}
+              </a>
+              <ul class={`s${rootInPath ? ' active' : ''}`}>
+                {root.children.map(child => {
+                  const childOff     = projectNodeOff(child, root.url);
+                  const childInPath  = inProjectSubtree(child, currentUrl);
+                  const childIsNode  = child.isNode;
+                  return (
+                    <li class={`s${childIsNode ? ' sub' : ''}${childOff ? ' off' : ''}`}>
+                      <a href={child.url} class={childInPath ? 's selected' : 's'}>
+                        {childInPath ? <strong>{child.title}</strong> : child.title}
+                      </a>
+                      {childIsNode && child.children.length > 0 && (
+                        <ul class={`s${childInPath ? ' active' : ''}`}>
+                          {child.children.map(grand => {
+                            const grandOff    = projectNodeOff(grand, child.url);
+                            const grandInPath = inProjectSubtree(grand, currentUrl);
+                            const grandIsNode = grand.isNode;
+                            return (
+                              <li class={`s${grandIsNode ? ' sub' : ''}${grandOff ? ' off' : ''}`}>
+                                <a href={grand.url} class={grandInPath ? 's selected' : 's'}>
+                                  {grandInPath ? <strong>{grand.title}</strong> : grand.title}
+                                </a>
+                                {grandIsNode && grand.children.length > 0 && (
+                                  <ul class={`s${grandInPath ? ' active' : ''}`}>
+                                    {grand.children.map(ggrand => {
+                                      const ggOff    = projectNodeOff(ggrand, grand.url);
+                                      const ggActive = ggrand.url === currentUrl;
+                                      return (
+                                        <li class={`s${ggOff ? ' off' : ''}`}>
+                                          <a href={ggrand.url} class={ggActive ? 's selected' : 's'}>
+                                            {ggActive ? <strong>{ggrand.title}</strong> : ggrand.title}
+                                          </a>
+                                        </li>
+                                      );
+                                    })}
+                                  </ul>
+                                )}
+                              </li>
+                            );
+                          })}
+                        </ul>
+                      )}
+                    </li>
+                  );
+                })}
+              </ul>
+            </li>
+          );
+        })}
+      </ul>
+    </li>
+
     {/* ── About subtree ────────────────────────────────────────────────────── */}
     <li class={`s sub${aboutOff ? ' off' : ''}`}>
       <a href="/about.html" class={onAboutSection ? 's selected' : 's'}>
diff --git a/src/components/ProjectPage.astro b/src/components/ProjectPage.astro
new file mode 100644 (file)
index 0000000..a852646
--- /dev/null
@@ -0,0 +1,39 @@
+---
+import { getEntry } from 'astro:content';
+import BaseLayout from '../layouts/BaseLayout.astro';
+import Breadcrumb from '../components/Breadcrumb.astro';
+import BlogNav from '../components/BlogNav.astro';
+
+interface Props {
+  entryId: string;
+}
+
+const { entryId } = Astro.props;
+const entry = await getEntry('projects', entryId);
+if (!entry) throw new Error(`Project entry not found: ${entryId}`);
+
+const { title, url, params } = entry.data;
+const canonical = params.canonical
+  ? new URL(params.canonical, Astro.site).toString()
+  : undefined;
+
+const crumbs = [
+  { title: 'Home', url: '/' },
+  { title: 'Projects', url: '/projects/' },
+  { title },
+];
+---
+
+<BaseLayout title={title} canonical={canonical}>
+  <Fragment slot="breadcrumb">
+    <Breadcrumb crumbs={crumbs} />
+  </Fragment>
+
+  <Fragment slot="article">
+    <div set:html={entry.body} />
+  </Fragment>
+
+  <Fragment slot="menu">
+    <BlogNav currentUrl={url} />
+  </Fragment>
+</BaseLayout>
diff --git a/src/pages/hibernate-maven-plugin/configuration.html.astro b/src/pages/hibernate-maven-plugin/configuration.html.astro
new file mode 100644 (file)
index 0000000..7560c8e
--- /dev/null
@@ -0,0 +1,4 @@
+---
+import ProjectPage from '../../components/ProjectPage.astro';
+---
+<ProjectPage entryId="hibernate-maven-plugin/2.1.1/configuration.html" />
diff --git a/src/pages/hibernate-maven-plugin/create-mojo.html.astro b/src/pages/hibernate-maven-plugin/create-mojo.html.astro
new file mode 100644 (file)
index 0000000..4a82776
--- /dev/null
@@ -0,0 +1,4 @@
+---
+import ProjectPage from '../../components/ProjectPage.astro';
+---
+<ProjectPage entryId="hibernate-maven-plugin/2.1.1/project-reports/plugin-info/create-mojo.html" />
diff --git a/src/pages/hibernate-maven-plugin/index.astro b/src/pages/hibernate-maven-plugin/index.astro
new file mode 100644 (file)
index 0000000..9b7098b
--- /dev/null
@@ -0,0 +1,4 @@
+---
+import ProjectPage from '../../components/ProjectPage.astro';
+---
+<ProjectPage entryId="hibernate-maven-plugin/2.1.1/_index.html" />
diff --git a/src/pages/hibernate-maven-plugin/plugin-info.html.astro b/src/pages/hibernate-maven-plugin/plugin-info.html.astro
new file mode 100644 (file)
index 0000000..6914de2
--- /dev/null
@@ -0,0 +1,4 @@
+---
+import ProjectPage from '../../components/ProjectPage.astro';
+---
+<ProjectPage entryId="hibernate-maven-plugin/2.1.1/project-reports/plugin-info/_index.html" />
diff --git a/src/pages/hibernate-maven-plugin/project-info.html.astro b/src/pages/hibernate-maven-plugin/project-info.html.astro
new file mode 100644 (file)
index 0000000..de2067f
--- /dev/null
@@ -0,0 +1,4 @@
+---
+import ProjectPage from '../../components/ProjectPage.astro';
+---
+<ProjectPage entryId="hibernate-maven-plugin/2.1.1/project-info/_index.html" />
diff --git a/src/pages/hibernate-maven-plugin/project-reports.html.astro b/src/pages/hibernate-maven-plugin/project-reports.html.astro
new file mode 100644 (file)
index 0000000..eb8585c
--- /dev/null
@@ -0,0 +1,4 @@
+---
+import ProjectPage from '../../components/ProjectPage.astro';
+---
+<ProjectPage entryId="hibernate-maven-plugin/2.1.1/project-reports/_index.html" />
diff --git a/src/pages/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/configuration.html.astro b/src/pages/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/configuration.html.astro
new file mode 100644 (file)
index 0000000..a4d65cd
--- /dev/null
@@ -0,0 +1,4 @@
+---
+import ProjectPage from '../../../../components/ProjectPage.astro';
+---
+<ProjectPage entryId="hibernate-maven-plugin/2.1.2-SNAPSHOT/configuration.html" />
diff --git a/src/pages/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/create-mojo.html.astro b/src/pages/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/create-mojo.html.astro
new file mode 100644 (file)
index 0000000..e4ce854
--- /dev/null
@@ -0,0 +1,4 @@
+---
+import ProjectPage from '../../../../components/ProjectPage.astro';
+---
+<ProjectPage entryId="hibernate-maven-plugin/2.1.2-SNAPSHOT/project-reports/plugin-info/create-mojo.html" />
diff --git a/src/pages/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/index.astro b/src/pages/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/index.astro
new file mode 100644 (file)
index 0000000..3c2e095
--- /dev/null
@@ -0,0 +1,4 @@
+---
+import ProjectPage from '../../../../components/ProjectPage.astro';
+---
+<ProjectPage entryId="hibernate-maven-plugin/2.1.2-SNAPSHOT/_index.html" />
diff --git a/src/pages/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/plugin-info.html.astro b/src/pages/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/plugin-info.html.astro
new file mode 100644 (file)
index 0000000..32aff09
--- /dev/null
@@ -0,0 +1,4 @@
+---
+import ProjectPage from '../../../../components/ProjectPage.astro';
+---
+<ProjectPage entryId="hibernate-maven-plugin/2.1.2-SNAPSHOT/project-reports/plugin-info/_index.html" />
diff --git a/src/pages/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/project-info.html.astro b/src/pages/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/project-info.html.astro
new file mode 100644 (file)
index 0000000..e0af676
--- /dev/null
@@ -0,0 +1,4 @@
+---
+import ProjectPage from '../../../../components/ProjectPage.astro';
+---
+<ProjectPage entryId="hibernate-maven-plugin/2.1.2-SNAPSHOT/project-info/_index.html" />
diff --git a/src/pages/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/project-reports.html.astro b/src/pages/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/project-reports.html.astro
new file mode 100644 (file)
index 0000000..5021b71
--- /dev/null
@@ -0,0 +1,4 @@
+---
+import ProjectPage from '../../../../components/ProjectPage.astro';
+---
+<ProjectPage entryId="hibernate-maven-plugin/2.1.2-SNAPSHOT/project-reports/_index.html" />
diff --git a/src/pages/projects/index.astro b/src/pages/projects/index.astro
new file mode 100644 (file)
index 0000000..e7930bb
--- /dev/null
@@ -0,0 +1,31 @@
+---
+import BaseLayout from '../../layouts/BaseLayout.astro';
+import Breadcrumb from '../../components/Breadcrumb.astro';
+import BlogNav from '../../components/BlogNav.astro';
+import { getVisibleProjectNavTrees } from '../../lib/projects';
+
+const projects = await getVisibleProjectNavTrees();
+const crumbs = [{ title: 'Home', url: '/' }, { title: 'Projects' }];
+---
+
+<BaseLayout title="Projects">
+  <Fragment slot="breadcrumb">
+    <Breadcrumb crumbs={crumbs} />
+  </Fragment>
+
+  <Fragment slot="article">
+    <header><h1>Projects</h1></header>
+    <div class="pack bg cf">
+      {projects.map(p => (
+        <div class="p">
+          <h2>{p.navRoot.title}</h2>
+          <p><a href={p.navRoot.url}>Visit documentation</a></p>
+        </div>
+      ))}
+    </div>
+  </Fragment>
+
+  <Fragment slot="menu">
+    <BlogNav currentUrl="/projects/" />
+  </Fragment>
+</BaseLayout>