same base for all, removed teaser image from top of articles removed boolean flag

This commit is contained in:
2025-08-30 17:36:29 +02:00
parent 452ca1770c
commit d642d8be98
40 changed files with 207 additions and 223 deletions

View File

@@ -42,21 +42,16 @@ Each `.mdx` file must contain a YAML frontmatter block at the top. Here are the
- `title` (string, required): The title of the post.
- `tags` (string[], optional): A list of relevant tags.
- `teaser` (string, required): A short, one-sentence summary of the content.
- `header` (object, optional): Used to specify a header image.
- `teaser` (string): Path to the teaser image (e.g., `/figures/my-image.png`).
- `show_teaser` (boolean, optional): If `false`, the teaser image will not be displayed on the post's page.
- `excerpt` (string, required): A short, one-sentence summary of the content.
- `teaser` (string): Path to the teaser image (e.g., `/figures/my-image.png`).
**Example Frontmatter:**
```yaml
---
title: "MAS Emergence Safety"
tags: [multi-agent-systems, MARL, safety]
teaser: "Formalized MAS emergence misalignment; proposed safety mitigation strategies."
header:
teaser: "/figures/21_coins_teaser.png"
show_teaser: false
excerpt: "Formalized MAS emergence misalignment; proposed safety mitigation strategies."
teaser: "/figures/21_coins_teaser.png"
---
```

View File

@@ -4,7 +4,7 @@ tags: [projects]
excerpt: "Early-stage mobile/distributed tech transfer between academia and industry (Bavaria)."
teaser: /images/projects/innomi.png
icon: /images/projects/innomi.png
show_teaser: false
---
The InnoMi research initiative served as a vital bridge between academic research and industrial application within Bavaria. Funded by the state government and operating under the umbrella of the Zentrum Digitalisierung.Bayern, the project provided crucial resources and a collaborative framework.

View File

@@ -4,7 +4,7 @@ tags: [projects]
excerpt: "Led online editorial team for DIGITALE WELT Magazin (2018-2023)."
teaser: "/images/projects/dw.png"
icon: "/images/projects/dw.png"
show_teaser: false
---
During my doctoral studies and research tenure at LMU Munich, I led the online editorial team for *DIGITALE WELT Magazin*. This role, supported by the [InnoMi project](/projects/innomi/), involved managing the publication's digital presence and strategic direction, aiming to effectively bridge scientific research and industry perspectives on digitalization trends.

View File

@@ -6,6 +6,8 @@ teaser: "/images/projects/pipe_leak.png"
icon: "/images/projects/pipe_leak.png"
---
In collaboration with Munich's municipal utility provider, Stadtwerke München (SWM), this project explored the feasibility of using acoustic monitoring for early leak detection in water pipe infrastructure. The primary goal was to develop machine learning models capable of identifying leak-indicating sound patterns within a real-world operational environment.
<InfoBox title="Project Details">
- **Project**: ErLoWa (Erkennung von Leckagen in Wasserleitungsnetzen)
- **Partner**: [Stadtwerke München (SWM)](https://www.swm.de/)
@@ -14,8 +16,6 @@ icon: "/images/projects/pipe_leak.png"
- **Skills**: Acoustic Signal Processing, Deep Learning (CNNs), Anomaly Detection, Real-world Data Handling, Sensor Data Analysis
</InfoBox>
In collaboration with Munich's municipal utility provider, Stadtwerke München (SWM), this project explored the feasibility of using acoustic monitoring for early leak detection in water pipe infrastructure. The primary goal was to develop machine learning models capable of identifying leak-indicating sound patterns within a real-world operational environment.
The objective was to investigate and develop methods for the automated detection and localization of leaks in urban water distribution networks using acoustic sensor data.
## Methodology & Activities

View File

@@ -6,6 +6,8 @@ teaser: "/images/projects/openmunich.png"
icon: "/images/projects/openmunich.png"
---
Organized by the LMU Chair for Mobile and Distributed Systems in collaboration with industry partners Accenture and Red Hat, the OpenMunich conference series created a forum for professionals, researchers, and students to engage with the latest developments in the open-source ecosystem.
<InfoBox title="Project Overview">
- **Role:** Lead Conference Organizer
- **Event:** [OpenMunich Conference](https://openmunich.eu)
@@ -15,8 +17,6 @@ icon: "/images/projects/openmunich.png"
- **Skills:** Event Management, Stakeholder Coordination, Project Planning, Website Management
</InfoBox>
Organized by the LMU Chair for Mobile and Distributed Systems in collaboration with industry partners Accenture and Red Hat, the OpenMunich conference series created a forum for professionals, researchers, and students to engage with the latest developments in the open-source ecosystem.
As Lead Organizer for the 2018 and 2019 editions, I was responsible for the end-to-end planning and execution of the events. The conference offered a platform to showcase university research—covering topics from Machine Learning to Quantum Computing—alongside practical insights and technology demonstrations from our industry partners.
## Key Responsibilities

View File

@@ -8,7 +8,8 @@ role: Researcher, Software Developer
skills: Multi-Agent Reinforcement Learning (MARL), Emergence Analysis, AI Safety, Simulation Environment Design, Python, Gymnasium API, Software Engineering, Unity (Visualization), Industry Collaboration
---
{/* The InfoBox now contains ALL metadata, creating a clean sidebar. */}
In collaboration with Fraunhofer IKS, the AI-Fusion project addressed the critical challenge of understanding and ensuring safety in multi-agent reinforcement learning (MARL) systems. Emergence, defined as the arising of complex, often unpredictable, system-level dynamics from local interactions between agents and their environment, was a central focus due to its implications for system safety and reliability. The project's objective was to investigate the detection and mitigation of potentially unsafe emergent behaviors in complex systems composed of multiple interacting AI agents, particularly in scenarios involving heterogeneous agents (e.g., mixed-vendor autonomous systems).
<InfoBox title="Project Details">
{/* Section 2: Project Info */}
<h4>Overview</h4>
@@ -32,9 +33,6 @@ skills: Multi-Agent Reinforcement Learning (MARL), Emergence Analysis, AI Safety
</ul>
</InfoBox>
{/* All the main content now flows naturally in a single column. */}
In collaboration with Fraunhofer IKS, the AI-Fusion project addressed the critical challenge of understanding and ensuring safety in multi-agent reinforcement learning (MARL) systems. Emergence, defined as the arising of complex, often unpredictable, system-level dynamics from local interactions between agents and their environment, was a central focus due to its implications for system safety and reliability. The project's objective was to investigate the detection and mitigation of potentially unsafe emergent behaviors in complex systems composed of multiple interacting AI agents, particularly in scenarios involving heterogeneous agents (e.g., mixed-vendor autonomous systems).
To facilitate research into these phenomena, key contributions included the development of specialized simulation tools:
**1. High-Performance MARL Simulation Environment:**

View File

@@ -6,6 +6,10 @@ teaser: "/images/projects/arch.png"
icon: "/images/projects/arch.png"
---
During my tenure at the LMU Chair for Mobile and Distributed Systems, alongside my research activities, I assumed responsibility for the ongoing maintenance of the group's IT infrastructure. This encompassed Linux workstations, Windows Server-based hypervisors, Linux file servers (utilizing ZFS), and core network services.
---
<InfoBox title="Project Overview">
- **Role**: System Administrator, DevOps Engineer, Network Administrator
- **Affiliation**: Chair for Mobile and Distributed Systems, LMU Munich
@@ -13,9 +17,6 @@ icon: "/images/projects/arch.png"
- **Technologies**: Kubernetes (K3S), Ansible, Docker, CI/CD (GitLab CI, Argo CD), GitOps, Linux Server Administration, Traefik, WireGuard, ZFS, Longhorn, IaC
</InfoBox>
During my tenure at the LMU Chair for Mobile and Distributed Systems, alongside my research activities, I assumed responsibility for the ongoing maintenance of the group's IT infrastructure. This encompassed Linux workstations, Windows Server-based hypervisors, Linux file servers (utilizing ZFS), and core network services.
---
## Key Initiatives & Achievements

View File

@@ -3,7 +3,7 @@ title: "Learned Trajectory Annotation"
tags: [research, geoinformatics, machine-learning, unsupervised-learning, human-robot-interaction, autoencoder]
excerpt: "Unsupervised autoencoder learns spatial context from trajectory data for annotation."
teaser: "/figures/0_trajectory_reconstruction_teaser.png"
show_teaser: true
---
<div className="my-6">

View File

@@ -4,7 +4,7 @@ tags: [research, deep-learning, audio-classification, paralinguistics, speech-an
excerpt: "Deep learning audio baseline for Interspeech 2019 ComParE challenge."
teaser: "/figures/3_deep_neural_baselines_teaser.jpg"
icon: "/figures/3_deep_neural_baselines.jpg"
show_teaser: true
---
# Deep Audio Baselines

View File

@@ -3,7 +3,7 @@ title: "Soccer Team Vectors"
tags: [machine-learning, representation-learning, sports-analytics, similarity-search]
excerpt: "STEVE learns soccer team embeddings from match data for analysis."
teaser: "/figures/2_steve_algo.jpg"
show_teaser: true
---
<div className="md:float-right md:w-1/2 lg:w-2/5 md:ml-6 mb-4">

View File

@@ -3,7 +3,7 @@ title: "3D Primitive Segmentation"
tags: [research, computer-vision, 3d-processing, point-clouds, segmentation, deep-learning, genetic-algorithms]
excerpt: "Hybrid method segments/fits primitives in large 3D point clouds."
teaser: "/figures/4_point_cloud_segmentation_teaser.jpg"
show_teaser: true
---
<div className="my-6 text-center">

View File

@@ -3,7 +3,7 @@ title: "PEOC OOD Detection"
tags: [deep-reinforcement-learning, out-of-distribution-detection, safety, anomaly-detection]
excerpt: "PEOC uses policy entropy for OOD detection in deep RL."
teaser: "/figures/6_ood_pipeline.jpg"
show_teaser: true
---
<InfoBox title="Performance Comparison">

View File

@@ -3,7 +3,7 @@ title: "Surgical-Mask Detection"
tags: [research, audio-classification, deep-learning, data-augmentation, computer-vision, paralinguistics]
excerpt: "CNN mask detection in speech using augmented spectrograms."
teaser: "/figures/7_mask_models.jpg"
show_teaser: true
---
This study investigates the efficacy of various **data augmentation techniques** applied directly to **mel-spectrogram representations** of audio data for improving classification performance. The specific task addressed is the detection of surgical mask usage based on human speech signals, a relevant problem in paralinguistics and audio analysis.

View File

@@ -3,7 +3,7 @@ title: "Anomalous Sound Features"
tags: [research, anomaly-detection, audio-classification, deep-learning, transfer-learning, feature-extraction]
excerpt: "Pretrained networks extract features for anomalous industrial sound detection."
teaser: "/figures/8_anomalous_sound_teaser.jpg"
show_teaser: true
---
<div className="float-right w-2/5 ml-6 mb-4">

View File

@@ -4,7 +4,7 @@ tags: [research, deep-learning, audio-classification, bioacoustics, conservation
excerpt: "Deep BiLSTM classifies primate vocalizations for acoustic wildlife monitoring."
teaser: /figures/11_recurrent_primate_workflow.jpg
icon: /figures/11_recurrent_primate_workflow.jpg
show_teaser: true
---
# Primate Vocalization Classification

View File

@@ -3,7 +3,7 @@ title: "Audio Vision Transformer"
tags: [research, deep-learning, audio-classification, computer-vision, attention-mechanisms, transformers]
excerpt: "Vision Transformer on spectrograms for audio classification, with data augmentation."
teaser: /figures/12_vision_transformer_teaser.jpg
show_teaser: true
---
This research explores the application of the **Vision Transformer (ViT)** architecture, originally designed for image processing, to the domain of audio classification by operating on **mel-spectrogram representations**. The ViT's attention mechanisms offer a potentially powerful alternative to convolutional approaches for capturing relevant patterns in spectrogram data.

View File

@@ -3,7 +3,7 @@ title: "Tasked Self-Replication"
tags: [research, artificial-life, complex-systems, neural-networks, self-organization, multi-task-learning]
excerpt: "Self-replicating networks perform tasks, exploring stabilization in artificial chemistry."
teaser: "/figures/13_sr_teaser.jpg"
show_teaser: true
---
Building upon the concept of self-replicating neural networks, this research explores the integration of **auxiliary functional goals** alongside the primary objective of self-replication. The aim is to create networks that can not only reproduce their own weights but also perform useful computations or interact meaningfully with an environment simultaneously.

View File

@@ -3,7 +3,7 @@ title: "RNN Memory Limits"
tags: [research, deep-learning, recurrent-neural-networks, sequence-modeling, theoretical-ml]
excerpt: "Investigated memory limits of RNNs in recalling uncorrelated sequences."
teaser: "/figures/22_rnn_limits.png"
show_teaser: true
---
Recurrent Neural Networks (RNNs), including variants like Long Short-Term Memory (LSTM) and Gated Recurrent Units (GRU), are designed with the intent to capture temporal dependencies within sequential data. Their internal mechanisms allow information from previous time steps to influence current processing.

View File

@@ -3,7 +3,7 @@ title: "RL Anomaly Detection"
tags: [research, reinforcement-learning, anomaly-detection, safety, lifelong-learning, generalization]
excerpt: "Perspective on anomaly detection challenges and future in reinforcement learning."
teaser: "/figures/14_ad_rl_teaser.jpg"
show_teaser: true
---
Anomaly Detection (AD) is crucial for the safe deployment of Reinforcement Learning (RL) agents, especially in safety-critical applications where encountering unexpected or out-of-distribution situations can lead to catastrophic failures. This work provides a perspective on the state and future directions of AD research specifically tailored for the complexities inherent in RL.

View File

@@ -3,7 +3,7 @@ title: "Extended Self-Replication"
tags: [artificial-life, complex-systems, neural-networks, self-organization, dynamical-systems]
excerpt: "Journal extension: self-replication, noise robustness, emergence, dynamical system analysis."
teaser: "/figures/15_sr_journal_teaser.jpg"
show_teaser: true
---
# Extended Self-Replication

View File

@@ -3,7 +3,7 @@ title: "Organism Network Emergence"
tags: [artificial-life, complex-systems, neural-networks, self-organization, emergent-computation]
excerpt: "Self-replicating networks collaborate forming higher-level Organism Networks with emergent functionalities."
teaser: "/figures/16_on_teaser.jpg"
show_teaser: true
---
This research investigates the transition from simple self-replication to higher levels of organization by exploring how populations of basic, self-replicating neural network units can form **"Organism Networks" (ONs)** through **collaboration and emergent differentiation**. Moving beyond the replication of individual networks, the focus shifts to the collective dynamics and functional capabilities that arise when these units interact within a shared environment (akin to an "artificial chemistry").

View File

@@ -3,7 +3,7 @@ title: "Voronoi Data Augmentation"
tags: [research, data-augmentation, computer-vision, deep-learning, convolutional-neural-networks]
excerpt: "VoronoiPatches improves CNN robustness via non-linear recombination augmentation."
teaser: "/figures/17_vp_teaser.jpg"
show_teaser: true
---
Data augmentation is essential for improving the performance and generalization of Convolutional Neural Networks (CNNs), especially when training data is limited. This research introduces **VoronoiPatches (VP)**, a novel data augmentation algorithm based on the principle of **non-linear recombination** of image information.

View File

@@ -3,7 +3,7 @@ title: "Autoencoder Trajectory Compression"
tags: [research, deep-learning, recurrent-neural-networks, trajectory-analysis, data-compression, geoinformatics]
excerpt: "LSTM autoencoder better DP for trajectory compression (Fréchet/DTW)."
teaser: /figures/23_trajectory_model.png
show_teaser: true
---
The proliferation of location-aware mobile devices generates vast amounts of GPS trajectory data, necessitating efficient storage solutions. While various compression techniques aim to reduce data volume, preserving essential spatio-temporal information remains crucial.

View File

@@ -3,7 +3,7 @@ title: "Primate Subsegment Sorting"
tags: [bioacoustics, audio-classification, deep-learning, data-labeling, signal-processing]
excerpt: "Binary subsegment presorting improves noisy primate sound classification."
teaser: /figures/19_binary_primates_teaser.jpg
show_teaser: false
---
<div className="not-prose flex flex-col md:flex-row gap-8 items-start">

View File

@@ -3,7 +3,7 @@ title: "Aquarium MARL Environment"
tags: [multi-agent-reinforcement-learning, MARL, simulation, emergence, complex-systems]
excerpt: "Aquarium: Open-source MARL environment for predator-prey studies."
teaser: /figures/20_aquarium.png
show_teaser: true
---
The study of complex interactions using Multi-Agent Reinforcement Learning (MARL), particularly **predator-prey dynamics**, often requires specialized simulation environments. To streamline research and avoid redundant development efforts, we introduce **Aquarium**: a versatile, open-source MARL environment specifically designed for investigating predator-prey scenarios and related **emergent behaviors**.

View File

@@ -3,7 +3,7 @@ title: "MAS Emergence Safety"
tags: [multi-agent-systems, MARL, safety, emergence, system-specification]
excerpt: "Formalized MAS emergence misalignment; proposed safety mitigation strategies."
teaser: "/figures/21_coins_teaser.png"
show_teaser: false
---
Multi-Agent Systems (MAS), particularly those employing decentralized decision-making based on local information (common in MARL), can exhibit **emergent effects**. These phenomena, arising from complex interactions, range from minor behavioral quirks to potentially catastrophic system failures, posing significant **safety challenges**.

View File

@@ -4,7 +4,7 @@ tags: [teaching, computer architecture, LMU, coordination]
excerpt: "Served as a Teaching Assistant and Tutorial Coordinator for the LMU Computer Architecture course, managing tutors and curriculum for over 600 students."
teaser: "/images/teaching/computer_gear.png"
icon: "/images/teaching/computer_gear.png"
show_teaser: false
---
<InfoBox title="Role & Responsibilities">

View File

@@ -4,7 +4,7 @@ tags: [teaching, iot, mqtt, python, influxdb, distributed-systems, practical-cou
excerpt: "Designed and taught an IoT practical exercise using MQTT and Python for approximately 200 students."
teaser: "/images/teaching/server.png"
icon: "/images/teaching/server.png"
show_teaser: false
---
<InfoBox title="Project Details">

View File

@@ -4,7 +4,7 @@ tags: [teaching, python, programming, introductory-course, curriculum-developmen
excerpt: "Co-developed/taught intensive introductory Python course for 200 students."
teaser: /images/teaching/py.png
icon: /images/teaching/py.png
show_teaser: false
---
In preparation for the practical exercises within the [Internet of Things (IoT) lecture series](/teaching/IOT), we identified the need for foundational programming skills among the student cohort. Consequently, during the Winter Semester 2018/19, I **co-developed and co-taught** an intensive introductory course focused on the **[Python programming language](https://www.python.org/)**.

View File

@@ -4,7 +4,7 @@ tags: [teaching, supervision, mentoring, research, academic writing]
excerpt: "Supervised student research, writing, and presentation skills in Mobile/Distributed Systems, ML, and Quantum Computing."
teaser: "/images/teaching/thesis.png"
icon: "/images/teaching/thesis.png"
show_teaser: false
---
<InfoBox title="Seminar Details">

View File

@@ -4,7 +4,7 @@ tags: [teaching, android, java, kotlin, mobile-development, app-development, agi
excerpt: "Supervised MSP: teams built Android apps (Java/Kotlin) using agile."
teaser: "/images/teaching/android.png"
icon: "/images/teaching/android.png"
show_teaser: true
---
<InfoBox title="Course Details">

View File

@@ -3,7 +3,7 @@ title: "Your Post Title Here"
tags: [tag1, tag2, optional-tag]
excerpt: "A concise, one-sentence summary of your content."
teaser: "/figures/your-teaser-image.png" # Optional: Path to a teaser image
show_teaser: true # Optional: Set to false to hide the teaser image on the post page
# Optional: Set to false to hide the teaser image on the post page
---
# Introduction to Your Topic

View File

@@ -55,18 +55,6 @@ export default async function BlogPage({ params }: { params: { slug: string } })
</div>
)}
</header>
{post.frontmatter.show_teaser && post.frontmatter.teaser && (
<div className="my-6">
<Image
src={post.frontmatter.teaser}
alt={`${post.frontmatter.title} teaser image`}
width={800}
height={400}
className="rounded-lg"
/>
</div>
)}
<CustomMDX {...post.source} />

View File

@@ -1,12 +1,8 @@
import { getPostBySlug, getPostSlugs, getPostFrontmatter } from '@/lib/mdx'; // Assume getPostFrontmatter exists
import { getPostBySlug, getPostSlugs } from '@/lib/mdx';
import { notFound } from 'next/navigation';
import { DATA } from '@/data/resume';
import { getPublicationsData } from '@/lib/publications';
import { ProjectArticle } from '@/components/project-article';
// Helper function to get frontmatter for a slug (you may need to create this in lib/mdx.ts)
// It's a lighter version of getPostBySlug that only parses frontmatter.
// If you don't have it, we can just use getPostBySlug, it's just slightly less performant.
import { Article } from '@/components/Article'; // <-- Use the new component
export async function generateStaticParams() {
const slugs = getPostSlugs('projects');
@@ -23,28 +19,24 @@ export async function generateMetadata({ params }: { params: { slug: string } })
}
export default async function ProjectPage({ params }: { params: { slug: string } }) {
const { slug } = params;
const post = await getPostBySlug('projects', slug);
const post = await getPostBySlug('projects', params.slug);
const publications = getPublicationsData();
if (!post) {
notFound();
}
// --- Logic for Previous/Next Navigation ---
const allSlugs = getPostSlugs('projects'); // Or get them from your DATA file if they are ordered there
const currentIndex = allSlugs.findIndex((s) => s === slug);
// --- Navigation Logic ---
const allSlugs = getPostSlugs('projects');
const currentIndex = allSlugs.findIndex((s) => s === params.slug);
const prevSlug = currentIndex > 0 ? allSlugs[currentIndex - 1] : null;
const nextSlug = currentIndex < allSlugs.length - 1 ? allSlugs[currentIndex + 1] : null;
const prevPost = prevSlug ? await getPostBySlug('projects', prevSlug) : null;
const nextPost = nextSlug ? await getPostBySlug('projects', nextSlug) : null;
const navigation = {
prev: prevPost ? { slug: prevSlug, title: prevPost.frontmatter.title } : null,
next: nextPost ? { slug: nextSlug, title: nextPost.frontmatter.title } : null,
};
return <ProjectArticle post={post} publications={publications} navigation={navigation} />;
return <Article post={post} publications={publications} navigation={navigation} basePath="projects" />;
}

View File

@@ -1,12 +1,8 @@
import { getPostBySlug, getPostSlugs } from '@/lib/mdx';
import { CitationProvider } from '@/context/citation-context';
import { ReferencesContainer } from '@/components/references-container';
import { notFound } from 'next/navigation';
import Image from 'next/image';
import { DATA } from '@/data/resume';
import { getPublicationsData } from '@/lib/publications';
import { CustomMDX } from '@/components/custom-mdx';
import Link from 'next/link';
import { Article } from '@/components/Article'; // <-- Use the new component
export async function generateStaticParams() {
const slugs = getPostSlugs('research');
@@ -15,9 +11,7 @@ export async function generateStaticParams() {
export async function generateMetadata({ params }: { params: { slug: string } }) {
const post = await getPostBySlug('research', params.slug);
if (!post) {
return {};
}
if (!post) { return {}; }
return {
title: post.frontmatter.title,
description: post.frontmatter.description || DATA.description,
@@ -29,64 +23,20 @@ export default async function ResearchPage({ params }: { params: { slug: string
const publications = getPublicationsData();
if (!post) {
return notFound();
notFound();
}
return (
<CitationProvider publications={publications}>
<main className="flex flex-col min-h-[100dvh]">
<article className="prose prose-stone dark:prose-invert max-w-none">
<header className="mb-8">
<h1 className="text-4xl font-bold tracking-tighter sm:text-5xl">
{post.frontmatter.title}
</h1>
{post.frontmatter.excerpt && (
<p className="text-lg text-muted-foreground mt-2">{post.frontmatter.excerpt}</p>
)}
{post.frontmatter.icon && (
<div className="mt-4">
<Image
src={post.frontmatter.icon}
alt={`${post.frontmatter.title} icon`}
width={64}
height={64}
className="rounded-full"
/>
</div>
)}
</header>
// --- Navigation Logic ---
const allSlugs = getPostSlugs('research');
const currentIndex = allSlugs.findIndex((s) => s === params.slug);
const prevSlug = currentIndex > 0 ? allSlugs[currentIndex - 1] : null;
const nextSlug = currentIndex < allSlugs.length - 1 ? allSlugs[currentIndex + 1] : null;
const prevPost = prevSlug ? await getPostBySlug('research', prevSlug) : null;
const nextPost = nextSlug ? await getPostBySlug('research', nextSlug) : null;
const navigation = {
prev: prevPost ? { slug: prevSlug, title: prevPost.frontmatter.title } : null,
next: nextPost ? { slug: nextSlug, title: nextPost.frontmatter.title } : null,
};
{post.frontmatter.show_teaser && post.frontmatter.teaser && (
<div className="my-6">
<Image
src={post.frontmatter.teaser}
alt={`${post.frontmatter.title} teaser image`}
width={800}
height={400}
className="rounded-lg"
/>
</div>
)}
<CustomMDX {...post.source} />
{post.frontmatter.tags && (
<div className="flex flex-wrap gap-2 mt-8">
{post.frontmatter.tags.map((tag: string) => (
<Link
key={tag}
href={`/tags/${tag}`}
className="px-3 py-1 text-sm font-medium bg-secondary text-secondary-foreground rounded-full hover:bg-primary hover:text-primary-foreground transition-colors"
>
{tag}
</Link>
))}
</div>
)}
<ReferencesContainer />
</article>
</main>
</CitationProvider>
);
}
return <Article post={post} publications={publications} navigation={navigation} basePath="research" />;
}

View File

@@ -1,13 +1,8 @@
import { getPostBySlug, getPostSlugs } from '@/lib/mdx';
import { CitationProvider } from '@/context/citation-context';
import { ReferencesContainer } from '@/components/references-container';
import { notFound } from 'next/navigation';
import Image from 'next/image';
import { DATA } from '@/data/resume';
import { getPublicationsData } from '@/lib/publications';
import { CustomMDX } from '@/components/custom-mdx';
import Link from 'next/link';
import router from 'next/router';
import { Article } from '@/components/Article'; // <-- Use the new component
export async function generateStaticParams() {
const slugs = getPostSlugs('teaching');
@@ -16,9 +11,7 @@ export async function generateStaticParams() {
export async function generateMetadata({ params }: { params: { slug: string } }) {
const post = await getPostBySlug('teaching', params.slug);
if (!post) {
return {};
}
if (!post) { return {}; }
return {
title: post.frontmatter.title,
description: post.frontmatter.description || DATA.description,
@@ -30,64 +23,20 @@ export default async function TeachingPage({ params }: { params: { slug: string
const publications = getPublicationsData();
if (!post) {
return notFound();
notFound();
}
return (
<CitationProvider publications={publications}>
<main className="flex flex-col min-h-[100dvh]">
<article className="prose prose-stone dark:prose-invert max-w-none">
<header className="mb-8">
<h1 className="text-4xl font-bold tracking-tighter sm:text-5xl">
{post.frontmatter.title}
</h1>
{post.frontmatter.excerpt && (
<p className="text-lg text-muted-foreground mt-2">{post.frontmatter.excerpt}</p>
)}
{post.frontmatter.icon && (
<div className="mt-4">
<Image
src={post.frontmatter.icon}
alt={`${post.frontmatter.title} icon`}
width={64}
height={64}
className="rounded-full"
/>
</div>
)}
</header>
// --- Navigation Logic ---
const allSlugs = getPostSlugs('teaching');
const currentIndex = allSlugs.findIndex((s) => s === params.slug);
const prevSlug = currentIndex > 0 ? allSlugs[currentIndex - 1] : null;
const nextSlug = currentIndex < allSlugs.length - 1 ? allSlugs[currentIndex + 1] : null;
const prevPost = prevSlug ? await getPostBySlug('teaching', prevSlug) : null;
const nextPost = nextSlug ? await getPostBySlug('teaching', nextSlug) : null;
const navigation = {
prev: prevPost ? { slug: prevSlug, title: prevPost.frontmatter.title } : null,
next: nextPost ? { slug: nextSlug, title: nextPost.frontmatter.title } : null,
};
{post.frontmatter.show_teaser && post.frontmatter.teaser && (
<div className="my-6">
<Image
src={post.frontmatter.teaser}
alt={`${post.frontmatter.title} teaser image`}
width={800}
height={400}
className="rounded-lg"
/>
</div>
)}
<CustomMDX {...post.source} />
{post.frontmatter.tags && (
<div className="flex flex-wrap gap-2 mt-8">
{post.frontmatter.tags.map((tag: string) => (
<Link
key={tag}
href={`/tags/${tag}`}
className="px-3 py-1 text-sm font-medium bg-secondary text-secondary-foreground rounded-full hover:bg-primary hover:text-primary-foreground transition-colors"
>
{tag}
</Link>
))}
</div>
)}
<ReferencesContainer />
</article>
</main>
</CitationProvider>
);
}
return <Article post={post} publications={publications} navigation={navigation} basePath="teaching" />;
}

118
src/components/Article.tsx Normal file
View File

@@ -0,0 +1,118 @@
"use client";
import { CitationProvider } from '@/context/citation-context';
import { ReferencesContainer } from '@/components/references-container';
import Image from 'next/image';
import { CustomMDX } from '@/components/custom-mdx';
import Link from 'next/link';
import { Publication } from '@/lib/publications';
import { useAccentColor } from '@/context/accent-color-context';
import { ProjectNavigation } from './project-navigation';
// --- DEFINE UNIVERSAL PROP TYPES ---
interface Post {
source: any;
frontmatter: {
title: string;
excerpt?: string;
teaser?: string;
tags?: string[];
icon?: string;
};
}
interface NavigationLink {
slug: string;
title: string;
}
interface ArticleProps {
post: Post;
publications: Publication[];
// Navigation is now an optional prop
navigation?: {
prev: NavigationLink | null;
next: NavigationLink | null;
};
basePath: string; // Used for navigation and tags
}
/**
* An internal component to consume the PageLayoutContext.
* This allows us to conditionally render the icon based on whether
* an InfoBox is present in the MDX content.
*/
function ArticleContent({ post }: { post: Post }) {
const showIcon = post.frontmatter.icon;
return (
<div className="mt-8">
{showIcon ? (
<div className="flow-root">
<Image
src={post.frontmatter.icon!}
alt={`${post.frontmatter.title} icon`}
width={80}
height={80}
className="hidden md:block float-left mr-4 mb-2"
/>
<CustomMDX {...post.source} />
</div>
) : (
<div className={!post.frontmatter.icon ? "first-letter:float-left first-letter:mr-3 first-letter:text-5xl first-letter:font-bold first-letter:leading-none first-letter:text-primary first-letter:pr-1" : ""}>
<CustomMDX {...post.source} />
</div>
)}
</div>
);
}
/**
* The main reusable Article component for all slug-based pages.
*/
export function Article({ post, publications, navigation, basePath }: ArticleProps) {
const accentColor = useAccentColor();
return (
<CitationProvider publications={publications}>
<main className="flex flex-col min-h-[100dvh] py-8">
<article className="prose prose-stone dark:prose-invert max-w-none">
<header className="mb-8">
<h1 className="text-4xl font-bold tracking-tighter sm:text-5xl mb-2">
{post.frontmatter.title}
</h1>
{post.frontmatter.excerpt && (
<p className="text-lg text-muted-foreground mt-1">{post.frontmatter.excerpt}</p>
)}
</header>
<ArticleContent post={post} />
{post.frontmatter.tags && (
<div className="flex flex-wrap gap-2 mt-8">
{post.frontmatter.tags.map((tag: string) => (
<Link
key={tag}
href={`/tags/${tag}`}
className="px-3 py-1 text-sm font-medium bg-secondary text-secondary-foreground transition-colors"
onMouseOver={(e) => e.currentTarget.style.backgroundColor = accentColor}
onMouseOut={(e) => e.currentTarget.style.backgroundColor = ''}
>
{tag}
</Link>
))}
</div>
)}
<ReferencesContainer />
{/* Conditionally render navigation if the data is provided */}
{navigation && (
<ProjectNavigation prev={navigation.prev} next={navigation.next} basePath={basePath} />
)}
</article>
</main>
</CitationProvider>
);
}

View File

@@ -9,8 +9,9 @@ export function InfoBox({ title, children, className, ...props }: InfoBoxProps)
return (
<aside
className={cn(
// The `not-prose` class will now work correctly.
"not-prose relative mt-4 mb-8 md:float-right md:w-64 md:ml-4 md:mb-4 p-4 border rounded-lg shadow-sm bg-card text-card-foreground",
"not-prose relative mt-4 mb-8 p-4 border rounded-lg shadow-sm bg-card text-card-foreground",
// --- FIX IS HERE: Tell the InfoBox to clear the left float on desktop ---
"md:float-right md:w-64 md:ml-4 md:mb-4 md:clear-left",
className
)}
{...props}

View File

@@ -1,3 +1,5 @@
"use client;"
import Image from "next/image";
import Link from "next/link";
import React from "react";

View File

@@ -15,7 +15,6 @@ interface Post {
frontmatter: {
title: string;
excerpt?: string;
show_teaser?: boolean;
teaser?: string;
tags?: string[];
icon?: string;
@@ -53,18 +52,6 @@ export function ProjectArticle({ post, publications, navigation, basePath }: Pro
)}
</header>
{post.frontmatter.show_teaser && post.frontmatter.teaser && (
<div className="my-6">
<Image
src={post.frontmatter.teaser}
alt={`${post.frontmatter.title} teaser image`}
width={800}
height={400}
className="rounded-lg"
/>
</div>
)}
{post.frontmatter.tags && (
<div className="flex flex-wrap gap-2 mt-8">
{post.frontmatter.tags.map((tag: string) => (
@@ -81,19 +68,22 @@ export function ProjectArticle({ post, publications, navigation, basePath }: Pro
</div>
)}
{/* --- FIX IS HERE: Reverted to the float-based layout for proper text wrapping --- */}
<div className="mt-8">
{post.frontmatter.icon ? (
// Using a div with a clearing utility ('flow-root') is a robust way to contain floats.
<div className="flow-root">
<Image
src={post.frontmatter.icon}
alt={`${post.frontmatter.title} icon`}
width={80}
height={80}
className="float-left mr-4 mb-2"
className="md:block float-left mr-4 mb-2"
/>
<CustomMDX {...post.source} />
</div>
) : (
// If no icon, fall back to the original large first-letter layout
<div className="first-letter:float-left first-letter:mr-3 first-letter:text-5xl first-letter:font-bold first-letter:leading-none first-letter:text-primary first-letter:pr-1">
<CustomMDX {...post.source} />
</div>