diff --git a/build.py b/build.py index cd9815a..d5093fc 100644 --- a/build.py +++ b/build.py @@ -21,6 +21,7 @@ from __future__ import annotations import argparse +import hashlib import html import re import sys @@ -94,9 +95,7 @@ def _parse_entries(text: str) -> list[dict]: entries.append(current) for e in entries: - e["body"] = " ".join( - l.strip() for l in e.pop("_body_lines") if l.strip() - ) + e["body"] = "\n".join(e.pop("_body_lines")).strip() return entries @@ -218,21 +217,82 @@ def render_projects(projects: list[Project]) -> str: return "\n".join(cards) +def _render_inline(text: str) -> str: + """Render inline markdown (links, bold) within an already-plaintext string.""" + result = "" + last_end = 0 + for m in re.finditer(r'\[([^\]]*)\]\(([^)]*)\)', text): + result += re.sub(r'\*\*([^*]+)\*\*', r'\1', + html.escape(text[last_end:m.start()])) + result += ( + f'' + f'{html.escape(m.group(1))}' + ) + last_end = m.end() + result += re.sub(r'\*\*([^*]+)\*\*', r'\1', + html.escape(text[last_end:])) + return result + + +def _render_bio(text: str) -> str: + """Convert plain text / simple markdown (bullets, links, bold) to HTML.""" + if not text.strip(): + return "" + lines = text.splitlines() + parts: list[str] = [] + in_list = False + para_lines: list[str] = [] + + def flush_para() -> None: + if para_lines: + parts.append(f'

{_render_inline(" ".join(para_lines))}

') + para_lines.clear() + + for line in lines: + stripped = line.strip() + is_bullet = stripped.startswith("- ") or stripped.startswith("* ") + if is_bullet: + flush_para() + if not in_list: + parts.append('") + in_list = False + if stripped: + para_lines.append(stripped) + else: + flush_para() + + if in_list: + parts.append("") + flush_para() + return "\n".join(parts) + + def render_collaborators(collaborators: list[Collaborator]) -> str: if not collaborators: return "

No collaborators found.

" cards = [] for c in collaborators: - # Avatar: photo if available, otherwise coloured initials + # Avatar: explicit photo > Gravatar (if email) > coloured initials + initials = "".join(w[0].upper() for w in c.name.split()[:2]) if c.picture: - initials = "".join(w[0].upper() for w in c.name.split()[:2]) avatar = ( f'' ) + elif c.email: + digest = hashlib.md5(c.email.strip().lower().encode()).hexdigest() + gravatar_url = f"https://www.gravatar.com/avatar/{digest}?s=200&d=mp" + avatar = ( + f'' + ) else: - initials = "".join(w[0].upper() for w in c.name.split()[:2]) avatar = f'
{initials}
' display_name = f"{c.title} {c.name}".strip() if c.title else c.name @@ -263,15 +323,15 @@ def render_collaborators(collaborators: list[Collaborator]) -> str: else: interests_html = "" - bio = f'

{html.escape(c.bio)}

' if c.bio else "" + bio = _render_bio(c.bio) cards.append(f"""\
{avatar}

{name_tag}

- {subtitle} {interests_html} + {subtitle} {bio}
""") diff --git a/index.html b/index.html index e373cfa..0accce9 100644 --- a/index.html +++ b/index.html @@ -331,7 +331,7 @@ /* ── Collaborators grid ── */ .collab-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); + grid-template-columns: 1fr; gap: 1.25rem; margin-top: 1.75rem; } @@ -350,12 +350,12 @@ .collab-card:hover { box-shadow: 0 6px 24px rgba(0,0,0,0.07); } .collab-avatar { - width: 64px; - height: 64px; + width: 110px; + height: 110px; border-radius: 50%; background: linear-gradient(135deg, #2563eb, #7c3aed); color: #fff; - font-size: 1rem; + font-size: 1.4rem; font-weight: 700; display: flex; align-items: center; @@ -364,8 +364,8 @@ } .collab-photo { - width: 64px; - height: 64px; + width: 110px; + height: 110px; border-radius: 50%; object-fit: cover; flex-shrink: 0; @@ -392,14 +392,16 @@ .collab-subtitle { font-size: 0.8rem; color: var(--muted); - margin-bottom: 0.45rem; + margin-top: 0.4rem; + margin-bottom: 0.4rem; } .collab-interests { display: flex; flex-wrap: wrap; gap: 0.3rem; - margin-bottom: 0.6rem; + margin-top: 0.35rem; + margin-bottom: 0; } .interest-tag { @@ -415,7 +417,28 @@ font-size: 0.85rem; color: var(--muted); line-height: 1.6; + margin-top: 0.5rem; + margin-bottom: 0; + } + + .collab-bio-list { + font-size: 0.85rem; + color: var(--muted); + line-height: 1.6; + margin: 0.5rem 0 0 1.1rem; + padding: 0; } + + .collab-bio-list li { margin-bottom: 0.2rem; } + + .collab-bio a, + .collab-bio-list a { + color: var(--accent); + text-decoration: none; + } + + .collab-bio a:hover, + .collab-bio-list a:hover { text-decoration: underline; } @@ -547,7 +570,10 @@

AI4SWEng – AI-Driven Software Engineering

Horizonongoing
2025–2028
-

European research project investigating how large language models and AI agents can assist and automate software engineering tasks, from requirements analysis and code generation to testing and maintenance. SIMLab contributes expertise in hybrid modelling and uncertainty quantification for AI-assisted development pipelines.

+

European research project investigating how large language models and AI agents can +assist and automate software engineering tasks, from requirements analysis and code +generation to testing and maintenance. SIMLab contributes expertise in hybrid modelling +and uncertainty quantification for AI-assisted development pipelines.

Project website →
@@ -556,7 +582,10 @@

CAPIA – AI-Based Cutting Tool Precision Control

Innosuisseongoing
2026–2027 · Eskenazi SA
-

Contrôle Autonome de la Précision des outils de coupe par Intelligence Artificielle. Innosuisse project with Eskenazi SA developing real-time machine-learning models for in-process monitoring and automatic correction of cutting-tool precision, reducing scrap rates and improving surface quality in high-precision machining.

+

Contrôle Autonome de la Précision des outils de coupe par Intelligence Artificielle. +Innosuisse project with Eskenazi SA developing real-time machine-learning models for +in-process monitoring and automatic correction of cutting-tool precision, reducing scrap +rates and improving surface quality in high-precision machining.

Project website →
@@ -565,7 +594,9 @@

JAXifer – Groundwater Level Forecasting

Etat du Valaisongoing
2023–2025
-

A JAX-based framework for 5-day groundwater level prediction from meteorological and weather forecast data. Uses differentiable hybrid models combining physics-based priors with data-driven components, enabling uncertainty-aware forecasts at regional scale.

+

A JAX-based framework for 5-day groundwater level prediction from meteorological and +weather forecast data. Uses differentiable hybrid models combining physics-based priors +with data-driven components, enabling uncertainty-aware forecasts at regional scale.

GitHub →
@@ -574,7 +605,10 @@

ML4HYDRO – Machine Learning for Hydroelectric Turbine Simulations

HES-SOongoing
2026
-

Domain-informed machine learning for the simulation of hydroelectric turbines. The project develops physics-constrained surrogate models that accurately replicate high-fidelity CFD simulations at a fraction of the computational cost, enabling rapid turbine optimisation and digital-twin applications for Swiss hydropower operators.

+

Domain-informed machine learning for the simulation of hydroelectric turbines. The +project develops physics-constrained surrogate models that accurately replicate +high-fidelity CFD simulations at a fraction of the computational cost, enabling rapid +turbine optimisation and digital-twin applications for Swiss hydropower operators.

@@ -583,7 +617,11 @@

Sovereign Spearphishing Detection

Innosuisseongoing
2026–2027 · Infomaniak
-

Innosuisse project with Infomaniak developing a sovereign, open-source solution for automated spearphishing detection. The system combines large language models with behavioural analysis to identify highly targeted email attacks without relying on third-party cloud infrastructure, addressing privacy and data-sovereignty requirements for Swiss organisations.

+

Innosuisse project with Infomaniak developing a sovereign, open-source solution for +automated spearphishing detection. The system combines large language models with +behavioural analysis to identify highly targeted email attacks without relying on +third-party cloud infrastructure, addressing privacy and data-sovereignty requirements +for Swiss organisations.

@@ -592,7 +630,10 @@

TrunX – Domain-Informed Tree Growth and Mortality Modelling

SNSFongoing
2026
-

SNSF Spark project developing domain-informed system-dynamics models of tree growth and mortality under changing climatic conditions. Combines differentiable mechanistic representations of carbon allocation and hydraulic failure with observational data to produce interpretable, uncertainty-aware forecasts of forest dynamics.

+

SNSF Spark project developing domain-informed system-dynamics models of tree growth and +mortality under changing climatic conditions. Combines differentiable mechanistic +representations of carbon allocation and hydraulic failure with observational data to +produce interpretable, uncertainty-aware forecasts of forest dynamics.

GitHub → @@ -608,8 +649,8 @@

Team

GM

Prof. Dr. Gregory Mermoud

-
Director
Hybrid ModelingDifferential ProgrammingUncertainty QuantificationDynamical Systems
+
Director

Gregory Mermoud is a professor at HES-SO Valais-Wallis and director of SIMLab. His research focuses on the intersection of physics-based modeling and machine learning, with an emphasis on developing interpretable and uncertainty-aware models for real-world engineering problems.

@@ -617,8 +658,8 @@

Prof. Dr.
CT

Dr. Cedric Travelletti

-
Senior Scientist
Gaussian ProcessesSpatial StatisticsInverse ProblemsGeosciences
+
Senior Scientist

Cedric Travelletti is a senior scientist at SIMLab specialising in probabilistic machine learning and spatial statistics. His work addresses inverse problems in geosciences, with a focus on scalable Gaussian process methods and uncertainty quantification for large-scale environmental applications.

@@ -626,8 +667,8 @@

GG

Glory Givi

-
Postdoc
Machine LearningExplainable AIScientific Computing
+
Postdoc
@@ -635,17 +676,23 @@

Glory Givi

MG

Dr. Marc Gillioz

-
Senior scientist
Deep LearningTime SeriesIndustrial Applications
-

Marc is currently working on projects related to hydroelectric power production, in particular applying data analysis and machine learning techniques to: - predict strain and fatigue for variable-speed turbines, - detect anomalies in operational data for better maintenance planning. At the HES-SO, Marc has also worked on problems related to power systems, such as power flow optimization through topological changes, modelling of hydroelectric production and high-voltage grids, and network reconstruction using Smart Meter data. Marc's background is in high-energy physics, with a stint in software engineering.

+
Senior scientist
+

Marc is currently working on projects related to hydroelectric power production, in particular applying data analysis and machine learning techniques to:

+ +

At the HES-SO, Marc has also worked on problems related to power systems, such as power flow optimization through topological changes, modelling of hydroelectric production and high-voltage grids, and network reconstruction using Smart Meter data.

+

Marc's background is in high-energy physics, with a stint in software engineering.

AV

Alexandre Veuthey

-
Research Engineer
Machine LearningComputer Vision
+
Research Engineer
@@ -653,8 +700,8 @@

Alexandre Veuthey

MR

Marta Rende

-
Assistant
+
Assistant
@@ -662,8 +709,8 @@

Marta Rende

DO

Dion Osmani

-
Assistant
Dynamic SystemsOptimization
+
Assistant