diff --git a/ADDING_PAGES.md b/ADDING_PAGES.md
index 69215f6f..04d41523 100644
--- a/ADDING_PAGES.md
+++ b/ADDING_PAGES.md
@@ -10,6 +10,7 @@ This guide explains how to add new pages to the Open Elements website, using the
## Overview
The Open Elements website uses a hybrid architecture:
+
- **Next.js** for page rendering and routing (`src/app/[locale]/`)
- **Markdown files** for content (`content/`)
- **i18n support** for English (EN) and German (DE) versions
@@ -31,11 +32,11 @@ content/
```markdown
---
-title: "Your Page Title"
-description: "Brief description for SEO and meta tags"
-layout: "article"
-url: "/your-page-name"
-keywords: ["keyword1", "keyword2", "keyword3"]
+title: 'Your Page Title'
+description: 'Brief description for SEO and meta tags'
+layout: 'article'
+url: '/your-page-name'
+keywords: ['keyword1', 'keyword2', 'keyword3']
---
Your page content here in Markdown format...
@@ -53,11 +54,11 @@ More content...
```markdown
---
-title: "Ihr Seitentitel"
-description: "Kurze Beschreibung für SEO und Meta-Tags"
-layout: "article"
-url: "/de/your-page-name"
-keywords: ["Schlüsselwort1", "Schlüsselwort2"]
+title: 'Ihr Seitentitel'
+description: 'Kurze Beschreibung für SEO und Meta-Tags'
+layout: 'article'
+url: '/de/your-page-name'
+keywords: ['Schlüsselwort1', 'Schlüsselwort2']
---
Ihr Seiteninhalt hier im Markdown-Format...
@@ -69,15 +70,15 @@ Inhalt für diesen Abschnitt...
### 2. Frontmatter Fields Explained
-| Field | Required | Description | Example Values |
-|-------|----------|-------------|----------------|
-| `title` | Yes | Page title (appears in browser tab and meta tags) | "DLT & Digital Trust Lecture" |
-| `description` | Yes | Page description for SEO and social sharing | "Since 2023 Hendrik Ebbers has been offering..." |
-| `layout` | Yes | Layout template to use | `"article"`, `"single"`, `"contact"`, `"about-us"` |
-| `url` | Yes | URL path for the page (EN: `/page-name`, DE: `/de/page-name`) | `/dlt-lecture` or `/de/dlt-lecture` |
-| `keywords` | No | SEO keywords | `["Java", "Open Source", "Support"]` |
-| `aliases` | No | Alternative URLs that redirect to this page | `['/old-url', '/another-old-url']` |
-| `newsletterPopup` | No | Whether to show newsletter popup | `true` or `false` |
+| Field | Required | Description | Example Values |
+| ----------------- | -------- | ------------------------------------------------------------- | -------------------------------------------------- |
+| `title` | Yes | Page title (appears in browser tab and meta tags) | "DLT & Digital Trust Lecture" |
+| `description` | Yes | Page description for SEO and social sharing | "Since 2023 Hendrik Ebbers has been offering..." |
+| `layout` | Yes | Layout template to use | `"article"`, `"single"`, `"contact"`, `"about-us"` |
+| `url` | Yes | URL path for the page (EN: `/page-name`, DE: `/de/page-name`) | `/dlt-lecture` or `/de/dlt-lecture` |
+| `keywords` | No | SEO keywords | `["Java", "Open Source", "Support"]` |
+| `aliases` | No | Alternative URLs that redirect to this page | `['/old-url', '/another-old-url']` |
+| `newsletterPopup` | No | Whether to show newsletter popup | `true` or `false` |
### 3. Available Layout Types
@@ -88,7 +89,7 @@ Choose the appropriate layout for your page:
- Best for: Text-heavy content pages, documentation
- **`single`** - Simple single-column layout
- - Used by: support-care-landingpage, support-care-temurin
+ - Used by: support-care-maven, support-care-temurin
- Best for: Landing pages, promotional content
- **`contact`** - Contact form layout
@@ -106,7 +107,7 @@ Choose the appropriate layout for your page:
- Used by: newsletter page
- **`index`** - Homepage layout
- - Used by: _index.md (homepage only)
+ - Used by: \_index.md (homepage only)
### 4. Create Next.js Page Component
@@ -121,25 +122,27 @@ src/app/[locale]/
#### Minimal Page Component Template
```tsx
-import { notFound } from 'next/navigation'
-import type { Metadata } from 'next'
+import { notFound } from 'next/navigation';
+import type { Metadata } from 'next';
interface YourPageProps {
params: Promise<{
- locale: string
- }>
+ locale: string;
+ }>;
}
-export async function generateMetadata({ params }: YourPageProps): Promise Wir freuen uns darauf, gemeinsam mit Ihnen die Zukunft von Apache Maven zu gestalten! We look forward to shaping the future of Apache Maven together with you!
{t.rich('intro', {
- hendrik: (chunks) => {chunks},
- oth: (chunks) => (
-
+ hendrik: chunks => {chunks},
+ oth: chunks => (
+
{chunks}
),
@@ -98,7 +103,7 @@ export default async function DltLecturePage({ params }: DltLecturePageProps) {
{t('excerptIntro')} {quoteText}
+ {quoteText}
+
{locale === 'de' ? 'Ihr Seitentitel' : 'Your Page Title'}
-
-
Über 500.000 Downloads pro Tag
TCK-zertifiziert, AQAvit-verifiziert, Community-getragen"
+ },
+ "maven": {
+ "name": "Apache Maven — Build & Dependency Management",
+ "desc": "Über 75 % aller Java-Projekte setzen auf Maven
Ca. 2 Milliarden Downloads jährlich"
+ },
+ "junit": {
+ "name": "JUnit — Testframework",
+ "desc": "Über 1 Milliarde Downloads pro Monat
Ca. 85 % Marktanteil im Java-Ökosystem"
+ },
+ "log4j": {
+ "name": "Apache Log4j — Logging",
+ "desc": "Ca. 76 % aller Java-Anwendungen nutzen Log4j
Geschäftskritisch für Protokollierung, Monitoring und Fehleranalyse"
+ },
+ "commons": {
+ "name": "Apache Commons — Standard-Libraries",
+ "desc": "Ca. 49 % der Java-Entwickler setzen Apache Commons aktiv ein
Modulare Sammlung: Lang, IO, Collections und weitere"
+ },
+ "componentsHighlight": "Kurz gesagt: Die essentielle Basis der technischen Vertrauenskette Ihrer Java-Anwendungen.",
+ "layersTitle": "Wo Support & Care ansetzt",
+ "layersIntro": "Java-Anwendungen lassen sich in drei Schichten gliedern:",
+ "pyramidAlt": "Die 3 Schichten einer Java-Anwendung",
+ "layer1": "Anwendungsspezifischer Code",
+ "layer1Desc": "Ihr individueller Geschäfts- und Fachlogik-Code. Diese Ebene ist höchst wertvoll, aber relativ klein im Umfang — sie baut auf Frameworks und Basistechnologien auf.",
+ "layer2": "Frameworks & Anwendungsplattformen",
+ "layer2Desc": "Spring Boot, Quarkus, Jakarta EE und andere. Für diese Ebene gibt es vielfach kommerziellen Support der jeweiligen Anbieter.",
+ "layer3": "Basiskomponenten — Hier setzt Support & Care an.",
+ "layer3Desc": "Laufzeitumgebung, Build- und Dependency-Management, Standardbibliotheken, Logging- und Test-Frameworks. Diese Komponenten kommen in praktisch jedem Java-Projekt vor — doch professionellen Support gibt es dafür bisher kaum.",
+ "layersHighlight": "Framework-Support allein reicht nicht. Die Log4Shell-Schwachstelle hat gezeigt: Eine kritische Sicherheitslücke in einer Basiskomponente kann Millionen von Anwendungen treffen — trotz aktueller Framework-Updates. Support & Care schließt genau diese Lücke.",
+ "servicesTitle": "Unsere Leistungen",
+ "servicesIntro": "Alle Leistungen werden direkt von den Maintainern und Committern der betreuten Projekte erbracht — nicht von einem nachgelagerten Support-Team.",
+ "lts": {
+ "name": "Long Term Support (LTS)",
+ "desc": "Weiterführung für die wichtigsten Versionen zur besseren Planung und Organisation Ihrer Updates. Sie müssen keine unsicheren oder nicht gewarteten Versionen einsetzen."
+ },
+ "security": {
+ "name": "Sicherheitsupdates & Bugfixes",
+ "desc": "Frühzeitige Informationen und Benachrichtigungen zu Schwachstellen und Patches. Schnelle Reaktionszeiten durch direkten Zugang zu den Entwicklern."
+ },
+ "documentation": {
+ "name": "Dokumentation & Transparenz",
+ "desc": "Unterstützung bei SBOM-Strategien und technischer Dokumentation — auf Deutsch oder Englisch. Transparente Nachvollziehbarkeit aller Änderungen."
+ },
+ "workshops": {
+ "name": "Workshops & Beratung",
+ "desc": "Direkter Austausch mit den Maintainern und Committern der Projekte — auf Deutsch oder Englisch. Individuelle Beratung zu Migration, Best Practices und Architekturentscheidungen."
+ },
+ "webinars": {
+ "name": "Regelmäßige Webinare & Statusupdates",
+ "desc": "Quartalsweise Webinare zu aktuellen Sicherheitsrisiken, wichtigen Versionsänderungen, Best-Practice-Empfehlungen und konkreten Auswirkungen auf Ihre OSS-Lieferkette."
+ },
+ "customBuilds": {
+ "name": "Kundenspezifische Builds & Tooling",
+ "desc": "Maßgeschneiderte Umsetzungen direkt durch die Maintainer — von speziellen Build-Konfigurationen bis zu individualisierten Tooling-Lösungen."
+ },
+ "craTitle": "Vorbereitet auf den Cyber Resilience Act",
+ "craP1": "Ab 2027 sind Hersteller im Rahmen des Cyber Resilience Act (CRA) für 100 % ihrer Software verantwortlich — einschließlich aller Open-Source-Abhängigkeiten. Das betrifft Patchzeiten, Schwachstellenmanagement, Dokumentation und langfristige Wartbarkeit. Open Elements agiert als Open-Source-Steward und gestaltet die regulatorischen Rahmenbedingungen aktiv mit. Als Gründungsmitglied der Open Regulatory Compliance Working Group (ORC WG) der Eclipse Foundation arbeiten wir gemeinsam mit führenden Open-Source-Foundations, großen Technologieunternehmen und Vertretern der EU an konkreten Spezifikationen und Praxisleitfäden zur CRA-Umsetzung.",
+ "craBulletTitle": "Support & Care hilft Ihnen konkret bei:",
+ "craBullet1": "Deutliche Reduzierung von Patchzeiten",
+ "craBullet2": "Systematische Schwachstellenüberwachung",
+ "craBullet3": "Planbare Verfügbarkeit von Updates",
+ "craBullet4": "Sicherstellung von Dokumentation und Transparenz (inkl. SBOM)",
+ "craBullet5": "Langfristige Wartbarkeitsgarantie",
+ "craBullet6": "Perspektivisch: CRA-konforme Attestierungen für betreute Projekte",
+ "craHighlight": "Open Elements ist Gründungsmitglied der ORC WG und arbeitet direkt an den Best Practices, die definieren, wie CRA-Compliance für Open-Source-Software umgesetzt wird. Diese Expertise fließt unmittelbar in Support & Care ein.",
+ "containersTitle": "Gehärtete Container für Staat und Verwaltung",
+ "containersP1": "Auch das ist Support & Care: Gehärtete Container für die deutsche Verwaltung.",
+ "containersP2": "Open Elements gehört zu einer exklusiven Gruppe von Organisationen, die gehärtete Container-Images für container.gov.de bereitstellen dürfen — neben dem Zentrum für Digitale Souveränität (ZenDiS) und dem Auswärtigen Amt. Für Support & Care Kunden bedeutet das: Die gehärteten Eclipse-Temurin-Images für alle aktuellen Java-LTS-Versionen (Java 11, 17, 21, 25+) sind Teil des Leistungsumfangs. Verifiziert, signiert und kontinuierlich gegen aktuelle Schwachstellendatenbanken abgeglichen.",
+ "containersBulletTitle": "Was gehärtete Container auszeichnet:",
+ "containersBullet1": "Verifizierte Herkunft und Qualitätssicherung",
+ "containersBullet2": "Aktuelle Abhängigkeiten ohne bekannte Schwachstellen",
+ "containersBullet3": "Software Bill of Materials (SBOM) für volle Transparenz",
+ "containersBullet4": "Kryptographische Signierung gegen Manipulation",
+ "containersBullet5": "Minimierte Angriffsfläche durch systematisches Hardening",
+ "containersImgAlt": "Open Elements liefert in Zukunft offizielle gehärtete Container-Images für die deutsche öffentliche Verwaltung",
+ "modelTitle": "Mehr als nur Support: Unser Modell",
+ "modelP1": "Support & Care funktioniert anders als klassischer Vendor-Support. Sie tragen gemeinsam mit uns die laufenden Pflege- und Verbesserungsaufwände für die betreuten Open-Source-Komponenten — offen, nachvollziehbar und messbar.",
+ "modelSubtitle": "Hier folgt Support & Care drei wichtigen Prinzipien:",
+ "modelPrinciple1": "Gelder fließen direkt an die Maintainer: Statt oberflächliche Support-Schichten darüber zu legen, investieren wir in die Vitalität des jeweiligen Projekt-Kerns. Die Menschen, die den Code tatsächlich pflegen, Sicherheitsupdates bereitstellen und neue Features entwickeln, werden direkt bezahlt.",
+ "modelPrinciple2": "Ihre Prioritäten in den Roadmaps: Kundenanforderungen werden aktiv in die Entwicklungs-Roadmaps der betreuten Projekte integriert. So spiegeln Weiterentwicklungen direkt reale Unternehmensbedürfnisse wider.",
+ "modelPrinciple3": "Proaktive Kommunikation: Sie werden nicht nur bei Problemen informiert, sondern kontinuierlich über relevante Entwicklungen auf dem Laufenden gehalten:",
+ "modelBullet1": "Sicherheitswarnungen und neue Patches",
+ "modelBullet2": "Geplante API- oder Major-Version-Änderungen",
+ "modelBullet3": "Empfehlungen zu Versionsupdates und Abhängigkeitsbereinigungen",
+ "modelBullet4": "Trends und Risiken im OSS-Ökosystem",
+ "modelHighlight": "Nicht genutzte Support-Stunden verfallen nicht — sie fließen direkt in die Weiterentwicklung der Open-Source-Komponenten. Jede Subscription stärkt die Projekte, auf die Sie sich verlassen.",
+ "modelP2": "Hierbei liefern wir flexible Leistungsmodelle für nachhaltige Sicherheit. Wählen Sie das Modell, das zu Ihren Anforderungen in Verfügbarkeit, Compliance und SLA passt.",
+ "whyTitle": "Warum Open Elements",
+ "whyP1": "Wir sind die Maintainer — nicht nur Berater: Unsere Mitarbeiter sind keine externen Consultants, die Projekte erst kennenlernen müssen. Sie sind die Menschen, die diese Projekte pflegen, weiterentwickeln und in den Foundations mitgestalten.",
+ "hendrikRole": "Founder & Eclipse Board Member",
+ "sandraRole": "Java Champion & OSS Maintainer",
+ "sebastianRole": "OSS Engineer & Maintainer Log4j",
+ "whyP2": "Open Elements ist ein bekanntes und aktives Mitglied der Open Source Community und arbeitet nicht nur auf technischer Basis sondern auch auf leitender Ebene in vielen Open Source Foundation mit:",
+ "eclipseDesc": "Wir sind im Board der Eclipse Foundation vertreten und aktives Mitglied in Arbeitsgruppen wie Eclipse Adoptium, Eclipse JakartaEE oder ORC WG.",
+ "lfDesc": "TODO",
+ "asfDesc": "TODO",
+ "whyHighlight": "Open Source — aber richtig. Unsere Einnahmen aus Support & Care fließen direkt in die betreuten Open-Source-Projekte.",
+ "faqTitle": "Häufig gestellte Fragen",
+ "faq1Q": "Ist Support & Care nur für Apache Maven?",
+ "faq1A": "Nein. Support & Care deckt fünf geschäftskritische Java-Basiskomponenten ab: Eclipse Temurin, Apache Maven, JUnit, Apache Log4j und Apache Commons. Das Programm startete 2024 mit Maven und wurde seitdem kontinuierlich erweitert.",
+ "faq2Q": "Wer leistet den Support?",
+ "faq2A": "Committer und Maintainer der jeweiligen Open-Source-Projekte — die Personen, die den Code tatsächlich schreiben und pflegen. Kein nachgelagertes Support-Team, sondern direkter Zugang zu den Experten.",
+ "faq3Q": "Was passiert mit meiner Subscription-Gebühr?",
+ "faq3A": "Die Einnahmen fließen transparent und nachvollziehbar in die betreuten Open-Source-Projekte: Bezahlung der Maintainer, Sicherheitsupdates, Bugfixes, Dokumentation und Infrastruktur.",
+ "faq4Q": "Muss ich alle fünf Komponenten abonnieren?",
+ "faq4A": "Sprechen Sie uns an — wir schneiden das Angebot auf Ihre konkreten Anforderungen zu.",
+ "faq5Q": "Hilft Support & Care bei der CRA-Compliance?",
+ "faq5A": "Ja. Support & Care adressiert zentrale CRA-Anforderungen: Schwachstellenüberwachung, Patchzeiten, Dokumentation, SBOM und langfristige Wartbarkeit. Perspektivisch unterstützen wir auch bei CRA-konformen Attestierungen.",
+ "faq6Q": "In welchen Sprachen wird Support geleistet?",
+ "faq6A": "Deutsch und Englisch — sowohl für Helpdesk-Anfragen als auch für Workshops, Beratung und Dokumentation.",
+ "faq7Q": "Was ist der Unterschied zu Framework-Support (z.B. bei Spring Boot)?",
+ "faq7A": "Framework-Support deckt die mittlere Schicht Ihres Software-Stacks ab. Support & Care betreut die Basisschicht darunter: Laufzeit, Build-Tools, Logging, Testing und Utility-Libraries. Beides ergänzt sich — Log4Shell hat gezeigt, dass Framework-Support allein nicht ausreicht.",
+ "ctaTitle": "Sichern Sie die Basis Ihrer Java-Anwendungen",
+ "ctaP1": "Lassen Sie uns gemeinsam besprechen, wie Support & Care Ihre Software-Lieferkette absichert. Ob Privatwirtschaft oder öffentliche Verwaltung — wir finden das passende Modell für Sie.",
+ "ctaContact": "Kontakt aufnehmen",
+ "footnote1": "Nicht genutzte Support-Stunden verfallen monatlich und fließen in die Weiterentwicklung der betreuten Projekte.",
+ "footnote2": "Werktage ohne Feiertage in NRW.",
+ "footnote3": "Helpdesk DSGVO-konform und EU-gehostet.",
+ "footnote4": "Expert:innen sind Committer und Maintainer der betreuten OSS-Projekte.",
+ "footnote5": "Webinare und Calls per Videokonferenz."
}
},
"blog": {
diff --git a/locales/en.json b/locales/en.json
index a6e77b42..488c9ea0 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -235,6 +235,146 @@
},
"subscription": {
"contactUs": "Contact us"
+ },
+ "landingpage": {
+ "logoAlt": "Support & Care Logo",
+ "heroParagraph": "Professional maintenance, security updates and long term support for the most business-critical open source components in the Java ecosystem — directly from the maintainers.",
+ "heroBody": "Modern software consists of over 70% open source components. From 2027, the Cyber Resilience Act (CRA) will hold manufacturers responsible for 100% of their software — including all OSS dependencies. Support & Care secures the foundation of your Java applications: from the runtime environment to build tools and testing strategy.",
+ "heroContact": "Get in touch",
+ "heroDiscover": "Discover our services",
+ "problemTitle": "The Problem: Invisible Dependencies",
+ "problemP1": "A simple Java project with Spring Boot brings over 70 transitive dependencies — most of them open source. Your individual code is just the tip of the iceberg. Underneath lie runtime environments, build tools, logging frameworks, test libraries and utility libraries that actually carry the operation of your application.",
+ "problemHighlight": "70% of software is based on open source and is therefore outside your direct control.",
+ "problemP2": "These foundational components are often maintained by individual developers in their spare time. At the same time, they carry the majority of technical risks: security vulnerabilities, transitive dependencies, missing documentation and compliance responsibility.",
+ "problemBulletTitle": "What this means for you:",
+ "problemBullet1": "Vulnerabilities in foundational components often go unnoticed until it is too late",
+ "problemBullet2Link": "Log4Shell",
+ "problemBullet2": "Framework support alone does not protect against gaps in the foundation — Log4Shell clearly demonstrated this",
+ "problemBullet3": "The CRA will hold you liable for the entire software supply chain from 2027",
+ "componentsTitle": "Supported Components",
+ "componentsIntro": "Support & Care specifically covers the five most business-critical open source foundational components of the Java ecosystem. Together, they form the technical chain of trust for virtually every Java application.",
+ "temurin": {
+ "name": "Eclipse Temurin — Java Runtime",
+ "desc": "Leading vendor-independent OpenJDK distribution worldwide
Over 500,000 downloads per day
TCK-certified, AQAvit-verified, community-driven"
+ },
+ "maven": {
+ "name": "Apache Maven — Build & Dependency Management",
+ "desc": "Over 75% of all Java projects use Maven
Approx. 2 billion downloads annually"
+ },
+ "junit": {
+ "name": "JUnit — Test Framework",
+ "desc": "Over 1 billion downloads per month
Approx. 85% market share in the Java ecosystem"
+ },
+ "log4j": {
+ "name": "Apache Log4j — Logging",
+ "desc": "Approx. 76% of all Java applications use Log4j
Business-critical for logging, monitoring and error analysis"
+ },
+ "commons": {
+ "name": "Apache Commons — Standard Libraries",
+ "desc": "Approx. 49% of Java developers actively use Apache Commons
Modular collection: Lang, IO, Collections and more"
+ },
+ "componentsHighlight": "In short: The essential foundation of the technical chain of trust for your Java applications.",
+ "layersTitle": "Where Support & Care Steps In",
+ "layersIntro": "Java applications can be divided into three layers:",
+ "pyramidAlt": "The 3 layers of a Java application",
+ "layer1": "Application-Specific Code",
+ "layer1Desc": "Your individual business and domain logic code. This layer is highly valuable but relatively small in scope — it builds on frameworks and foundational technologies.",
+ "layer2": "Frameworks & Application Platforms",
+ "layer2Desc": "Spring Boot, Quarkus, Jakarta EE and others. Commercial support from the respective vendors is widely available for this layer.",
+ "layer3": "Foundational Components — This is where Support & Care steps in.",
+ "layer3Desc": "Runtime environment, build and dependency management, standard libraries, logging and test frameworks. These components are used in virtually every Java project — yet professional support has been largely unavailable until now.",
+ "layersHighlight": "Framework support alone is not enough. The Log4Shell vulnerability demonstrated: A critical security flaw in a foundational component can affect millions of applications — despite up-to-date framework updates. Support & Care closes exactly this gap.",
+ "servicesTitle": "Our Services",
+ "servicesIntro": "All services are delivered directly by the maintainers and committers of the supported projects — not by a downstream support team.",
+ "lts": {
+ "name": "Long Term Support (LTS)",
+ "desc": "Continued support for the most important versions to help you better plan and organize your updates. You never have to rely on insecure or unmaintained versions."
+ },
+ "security": {
+ "name": "Security Updates & Bugfixes",
+ "desc": "Early information and notifications about vulnerabilities and patches. Fast response times through direct access to the developers."
+ },
+ "documentation": {
+ "name": "Documentation & Transparency",
+ "desc": "Support with SBOM strategies and technical documentation — in German or English. Transparent traceability of all changes."
+ },
+ "workshops": {
+ "name": "Workshops & Consulting",
+ "desc": "Direct exchange with the maintainers and committers of the projects — in German or English. Individual consulting on migration, best practices and architectural decisions."
+ },
+ "webinars": {
+ "name": "Regular Webinars & Status Updates",
+ "desc": "Quarterly webinars on current security risks, important version changes, best practice recommendations and concrete impacts on your OSS supply chain."
+ },
+ "customBuilds": {
+ "name": "Custom Builds & Tooling",
+ "desc": "Tailored implementations directly by the maintainers — from special build configurations to individualized tooling solutions."
+ },
+ "craTitle": "Prepared for the Cyber Resilience Act",
+ "craP1": "From 2027, manufacturers will be responsible for 100% of their software under the Cyber Resilience Act (CRA) — including all open source dependencies. This covers patch times, vulnerability management, documentation and long-term maintainability. Open Elements acts as an open source steward and actively shapes the regulatory framework. As a founding member of the Open Regulatory Compliance Working Group (ORC WG) of the Eclipse Foundation, we work together with leading open source foundations, major technology companies and EU representatives on concrete specifications and practical guidelines for CRA implementation.",
+ "craBulletTitle": "Support & Care specifically helps you with:",
+ "craBullet1": "Significant reduction of patch times",
+ "craBullet2": "Systematic vulnerability monitoring",
+ "craBullet3": "Predictable availability of updates",
+ "craBullet4": "Ensuring documentation and transparency (incl. SBOM)",
+ "craBullet5": "Long-term maintainability guarantee",
+ "craBullet6": "Prospectively: CRA-compliant attestations for supported projects",
+ "craHighlight": "Open Elements is a founding member of the ORC WG and works directly on the best practices that define how CRA compliance for open source software is implemented. This expertise flows directly into Support & Care.",
+ "containersTitle": "Hardened Containers for Government and Public Administration",
+ "containersP1": "This is also Support & Care: Hardened containers for the German public administration.",
+ "containersP2": "Open Elements belongs to an exclusive group of organizations authorized to provide hardened container images for container.gov.de — alongside the Center for Digital Sovereignty (ZenDiS) and the German Federal Foreign Office. For Support & Care customers, this means: The hardened Eclipse Temurin images for all current Java LTS versions (Java 11, 17, 21, 25+) are included in the service scope. Verified, signed and continuously checked against current vulnerability databases.",
+ "containersBulletTitle": "What distinguishes hardened containers:",
+ "containersBullet1": "Verified origin and quality assurance",
+ "containersBullet2": "Up-to-date dependencies without known vulnerabilities",
+ "containersBullet3": "Software Bill of Materials (SBOM) for full transparency",
+ "containersBullet4": "Cryptographic signing against tampering",
+ "containersBullet5": "Minimized attack surface through systematic hardening",
+ "containersImgAlt": "Open Elements delivers official hardened container images for the German public administration",
+ "modelTitle": "More Than Just Support: Our Model",
+ "modelP1": "Support & Care works differently from traditional vendor support. Together with us, you share the ongoing maintenance and improvement efforts for the supported open source components — openly, transparently and measurably.",
+ "modelSubtitle": "Support & Care follows three important principles:",
+ "modelPrinciple1": "Funds flow directly to the maintainers: Instead of layering superficial support on top, we invest in the vitality of each project's core. The people who actually maintain the code, provide security updates and develop new features are paid directly.",
+ "modelPrinciple2": "Your priorities in the roadmaps: Customer requirements are actively integrated into the development roadmaps of the supported projects. This way, enhancements directly reflect real business needs.",
+ "modelPrinciple3": "Proactive communication: You are not only informed when problems arise, but continuously kept up to date on relevant developments:",
+ "modelBullet1": "Security warnings and new patches",
+ "modelBullet2": "Planned API or major version changes",
+ "modelBullet3": "Recommendations for version updates and dependency cleanup",
+ "modelBullet4": "Trends and risks in the OSS ecosystem",
+ "modelHighlight": "Unused support hours do not expire — they flow directly into the further development of the open source components. Every subscription strengthens the projects you rely on.",
+ "modelP2": "We deliver flexible service models for sustainable security. Choose the model that fits your requirements in availability, compliance and SLA.",
+ "whyTitle": "Why Open Elements",
+ "whyP1": "We are the maintainers — not just consultants: Our team members are not external consultants who first need to get to know the projects. They are the people who maintain, develop and co-shape these projects within the foundations.",
+ "hendrikRole": "Founder & Eclipse Board Member",
+ "sandraRole": "Java Champion & OSS Maintainer",
+ "sebastianRole": "OSS Engineer & Maintainer Log4j",
+ "whyP2": "Open Elements is a well-known and active member of the open source community, contributing not only on a technical level but also in leadership roles across many open source foundations:",
+ "eclipseDesc": "We hold a seat on the Eclipse Foundation Board and are active members of working groups such as Eclipse Adoptium, Eclipse JakartaEE and the ORC WG.",
+ "lfDesc": "TODO",
+ "asfDesc": "TODO",
+ "whyHighlight": "Open source — done right. Our revenue from Support & Care flows directly into the supported open source projects.",
+ "faqTitle": "Frequently Asked Questions",
+ "faq1Q": "Is Support & Care only for Apache Maven?",
+ "faq1A": "No. Support & Care covers five business-critical Java foundational components: Eclipse Temurin, Apache Maven, JUnit, Apache Log4j and Apache Commons. The program started in 2024 with Maven and has been continuously expanded since then.",
+ "faq2Q": "Who provides the support?",
+ "faq2A": "Committers and maintainers of the respective open source projects — the people who actually write and maintain the code. No downstream support team, but direct access to the experts.",
+ "faq3Q": "What happens with my subscription fee?",
+ "faq3A": "The revenue flows transparently and traceably into the supported open source projects: payment of maintainers, security updates, bugfixes, documentation and infrastructure.",
+ "faq4Q": "Do I have to subscribe to all five components?",
+ "faq4A": "Get in touch — we tailor the offering to your specific requirements.",
+ "faq5Q": "Does Support & Care help with CRA compliance?",
+ "faq5A": "Yes. Support & Care addresses key CRA requirements: vulnerability monitoring, patch times, documentation, SBOM and long-term maintainability. Prospectively, we also support CRA-compliant attestations.",
+ "faq6Q": "In which languages is support provided?",
+ "faq6A": "German and English — for helpdesk requests as well as workshops, consulting and documentation.",
+ "faq7Q": "What is the difference to framework support (e.g. Spring Boot)?",
+ "faq7A": "Framework support covers the middle layer of your software stack. Support & Care covers the foundational layer underneath: runtime, build tools, logging, testing and utility libraries. Both complement each other — Log4Shell showed that framework support alone is not enough.",
+ "ctaTitle": "Secure the Foundation of Your Java Applications",
+ "ctaP1": "Let us discuss together how Support & Care can protect your software supply chain. Whether private sector or public administration — we will find the right model for you.",
+ "ctaContact": "Get in touch",
+ "footnote1": "Unused support hours expire monthly and flow into the further development of the supported projects.",
+ "footnote2": "Business days excluding public holidays in NRW.",
+ "footnote3": "Helpdesk GDPR-compliant and EU-hosted.",
+ "footnote4": "Experts are committers and maintainers of the supported OSS projects.",
+ "footnote5": "Webinars and calls via video conference."
}
},
"updates": {
diff --git a/package.json b/package.json
index e3b7b982..0f276536 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,8 @@
"build": "next build",
"start": "next start",
"lint": "eslint .",
+ "format:check": "prettier --check 'src/**'",
+ "format": "prettier --write 'src/**'",
"test:e2e": "playwright test"
},
"devDependencies": {
@@ -25,6 +27,7 @@
"eslint": "^9.39.2",
"eslint-config-next": "^16.1.0",
"eslint-config-prettier": "^10.1.8",
+ "eslint-plugin-prettier": "^5.5.5",
"npm-run-all": "^4.1.5",
"postcss": "^8.5.6",
"prettier": "^3.8.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a1f98651..4c10429f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -90,6 +90,9 @@ importers:
eslint-config-prettier:
specifier: ^10.1.8
version: 10.1.8(eslint@9.39.2(jiti@2.6.1))
+ eslint-plugin-prettier:
+ specifier: ^5.5.5
+ version: 5.5.5(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.8.0)
npm-run-all:
specifier: ^4.1.5
version: 4.1.5
@@ -449,105 +452,89 @@ packages:
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-arm@1.2.4':
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
cpu: [arm]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-ppc64@1.2.4':
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
cpu: [ppc64]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-riscv64@1.2.4':
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
cpu: [riscv64]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.2.4':
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
cpu: [s390x]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-x64@1.2.4':
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
cpu: [x64]
os: [linux]
- libc: [musl]
'@img/sharp-linux-arm64@0.34.5':
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-arm@0.34.5':
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-ppc64@0.34.5':
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ppc64]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-riscv64@0.34.5':
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [riscv64]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-s390x@0.34.5':
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-x64@0.34.5':
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@img/sharp-linuxmusl-arm64@0.34.5':
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@img/sharp-linuxmusl-x64@0.34.5':
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
- libc: [musl]
'@img/sharp-wasm32@0.34.5':
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
@@ -618,28 +605,24 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@next/swc-linux-arm64-musl@16.1.0':
resolution: {integrity: sha512-TojQnDRoX7wJWXEEwdfuJtakMDW64Q7NrxQPviUnfYJvAx5/5wcGE+1vZzQ9F17m+SdpFeeXuOr6v3jbyusYMQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@next/swc-linux-x64-gnu@16.1.0':
resolution: {integrity: sha512-quhNFVySW4QwXiZkZ34SbfzNBm27vLrxZ2HwTfFFO1BBP0OY1+pI0nbyewKeq1FriqU+LZrob/cm26lwsiAi8Q==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@next/swc-linux-x64-musl@16.1.0':
resolution: {integrity: sha512-6JW0z2FZUK5iOVhUIWqE4RblAhUj1EwhZ/MwteGb//SpFTOHydnhbp3868gxalwea+mbOLWO6xgxj9wA9wNvNw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- libc: [musl]
'@next/swc-win32-arm64-msvc@16.1.0':
resolution: {integrity: sha512-+DK/akkAvvXn5RdYN84IOmLkSy87SCmpofJPdB8vbLmf01BzntPBSYXnMvnEEv/Vcf3HYJwt24QZ/s6sWAwOMQ==}
@@ -698,42 +681,36 @@ packages:
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
- libc: [glibc]
'@parcel/watcher-linux-arm-musl@2.5.4':
resolution: {integrity: sha512-kGO8RPvVrcAotV4QcWh8kZuHr9bXi9a3bSZw7kFarYR0+fGliU7hd/zevhjw8fnvIKG3J9EO5G6sXNGCSNMYPQ==}
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
- libc: [musl]
'@parcel/watcher-linux-arm64-glibc@2.5.4':
resolution: {integrity: sha512-KU75aooXhqGFY2W5/p8DYYHt4hrjHZod8AhcGAmhzPn/etTa+lYCDB2b1sJy3sWJ8ahFVTdy+EbqSBvMx3iFlw==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@parcel/watcher-linux-arm64-musl@2.5.4':
resolution: {integrity: sha512-Qx8uNiIekVutnzbVdrgSanM+cbpDD3boB1f8vMtnuG5Zau4/bdDbXyKwIn0ToqFhIuob73bcxV9NwRm04/hzHQ==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@parcel/watcher-linux-x64-glibc@2.5.4':
resolution: {integrity: sha512-UYBQvhYmgAv61LNUn24qGQdjtycFBKSK3EXr72DbJqX9aaLbtCOO8+1SkKhD/GNiJ97ExgcHBrukcYhVjrnogA==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@parcel/watcher-linux-x64-musl@2.5.4':
resolution: {integrity: sha512-YoRWCVgxv8akZrMhdyVi6/TyoeeMkQ0PGGOf2E4omODrvd1wxniXP+DBynKoHryStks7l+fDAMUBRzqNHrVOpg==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
- libc: [musl]
'@parcel/watcher-win32-arm64@2.5.4':
resolution: {integrity: sha512-iby+D/YNXWkiQNYcIhg8P5hSjzXEHaQrk2SLrWOUD7VeC4Ohu0WQvmV+HDJokZVJ2UjJ4AGXW3bx7Lls9Ln4TQ==}
@@ -757,6 +734,10 @@ packages:
resolution: {integrity: sha512-WYa2tUVV5HiArWPB3ydlOc4R2ivq0IDrlqhMi3l7mVsFEXNcTfxYFPIHXHXIh/ca/y/V5N4E1zecyxdIBjYnkQ==}
engines: {node: '>= 10.0.0'}
+ '@pkgr/core@0.2.9':
+ resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
+ engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+
'@playwright/test@1.58.1':
resolution: {integrity: sha512-6LdVIUERWxQMmUSSQi0I53GgCBYgM2RpGngCPY7hSeju+VrKjq3lvs7HpJoPbDiY5QM5EYRtRX5fvrinnMAz3w==}
engines: {node: '>=18'}
@@ -791,28 +772,24 @@ packages:
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@swc/core-linux-arm64-musl@1.15.10':
resolution: {integrity: sha512-4uAHO3nbfbrTcmO/9YcVweTQdx5fN3l7ewwl5AEK4yoC4wXmoBTEPHAVdKNe4r9+xrTgd4BgyPsy0409OjjlMw==}
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@swc/core-linux-x64-gnu@1.15.10':
resolution: {integrity: sha512-W0h9ONNw1pVIA0cN7wtboOSTl4Jk3tHq+w2cMPQudu9/+3xoCxpFb9ZdehwCAk29IsvdWzGzY6P7dDVTyFwoqg==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@swc/core-linux-x64-musl@1.15.10':
resolution: {integrity: sha512-XQNZlLZB62S8nAbw7pqoqwy91Ldy2RpaMRqdRN3T+tAg6Xg6FywXRKCsLh6IQOadr4p1+lGnqM/Wn35z5a/0Vw==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
- libc: [musl]
'@swc/core-win32-arm64-msvc@1.15.10':
resolution: {integrity: sha512-qnAGrRv5Nj/DATxAmCnJQRXXQqnJwR0trxLndhoHoxGci9MuguNIjWahS0gw8YZFjgTinbTxOwzatkoySihnmw==}
@@ -893,28 +870,24 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@tailwindcss/oxide-linux-arm64-musl@4.1.18':
resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@tailwindcss/oxide-linux-x64-gnu@4.1.18':
resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@tailwindcss/oxide-linux-x64-musl@4.1.18':
resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- libc: [musl]
'@tailwindcss/oxide-wasm32-wasi@4.1.18':
resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==}
@@ -1094,49 +1067,41 @@ packages:
resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@unrs/resolver-binding-linux-arm64-musl@1.11.1':
resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
cpu: [ppc64]
os: [linux]
- libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
cpu: [riscv64]
os: [linux]
- libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
cpu: [riscv64]
os: [linux]
- libc: [musl]
'@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
cpu: [s390x]
os: [linux]
- libc: [glibc]
'@unrs/resolver-binding-linux-x64-gnu@1.11.1':
resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@unrs/resolver-binding-linux-x64-musl@1.11.1':
resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
cpu: [x64]
os: [linux]
- libc: [musl]
'@unrs/resolver-binding-wasm32-wasi@1.11.1':
resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}
@@ -1558,6 +1523,20 @@ packages:
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9
+ eslint-plugin-prettier@5.5.5:
+ resolution: {integrity: sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ '@types/eslint': '>=8.0.0'
+ eslint: '>=8.0.0'
+ eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0'
+ prettier: '>=3.0.0'
+ peerDependenciesMeta:
+ '@types/eslint':
+ optional: true
+ eslint-config-prettier:
+ optional: true
+
eslint-plugin-react-hooks@7.0.1:
resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==}
engines: {node: '>=18'}
@@ -1627,6 +1606,9 @@ packages:
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+ fast-diff@1.3.0:
+ resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
+
fast-glob@3.3.1:
resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
engines: {node: '>=8.6.0'}
@@ -2044,28 +2026,24 @@ packages:
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
- libc: [glibc]
lightningcss-linux-arm64-musl@1.30.2:
resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
- libc: [musl]
lightningcss-linux-x64-gnu@1.30.2:
resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
- libc: [glibc]
lightningcss-linux-x64-musl@1.30.2:
resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
- libc: [musl]
lightningcss-win32-arm64-msvc@1.30.2:
resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
@@ -2458,6 +2436,10 @@ packages:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
+ prettier-linter-helpers@1.0.1:
+ resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==}
+ engines: {node: '>=6.0.0'}
+
prettier@3.8.0:
resolution: {integrity: sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA==}
engines: {node: '>=14'}
@@ -2727,6 +2709,10 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
+ synckit@0.11.12:
+ resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+
tailwind-scrollbar@4.0.2:
resolution: {integrity: sha512-wAQiIxAPqk0MNTPptVe/xoyWi27y+NRGnTwvn4PQnbvB9kp8QUBiGl/wsfoVBHnQxTmhXJSNt9NHTmcz9EivFA==}
engines: {node: '>=12.13.0'}
@@ -3414,6 +3400,8 @@ snapshots:
'@parcel/watcher-win32-ia32': 2.5.4
'@parcel/watcher-win32-x64': 2.5.4
+ '@pkgr/core@0.2.9': {}
+
'@playwright/test@1.58.1':
dependencies:
playwright: 1.58.1
@@ -4296,6 +4284,15 @@ snapshots:
safe-regex-test: 1.1.0
string.prototype.includes: 2.0.1
+ eslint-plugin-prettier@5.5.5(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.8.0):
+ dependencies:
+ eslint: 9.39.2(jiti@2.6.1)
+ prettier: 3.8.0
+ prettier-linter-helpers: 1.0.1
+ synckit: 0.11.12
+ optionalDependencies:
+ eslint-config-prettier: 10.1.8(eslint@9.39.2(jiti@2.6.1))
+
eslint-plugin-react-hooks@7.0.1(eslint@9.39.2(jiti@2.6.1)):
dependencies:
'@babel/core': 7.28.5
@@ -4407,6 +4404,8 @@ snapshots:
fast-deep-equal@3.1.3: {}
+ fast-diff@1.3.0: {}
+
fast-glob@3.3.1:
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -5408,6 +5407,10 @@ snapshots:
prelude-ls@1.2.1: {}
+ prettier-linter-helpers@1.0.1:
+ dependencies:
+ fast-diff: 1.3.0
+
prettier@3.8.0: {}
prism-react-renderer@2.4.1(react@19.2.3):
@@ -5770,6 +5773,10 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
+ synckit@0.11.12:
+ dependencies:
+ '@pkgr/core': 0.2.9
+
tailwind-scrollbar@4.0.2(react@19.2.3)(tailwindcss@4.1.18):
dependencies:
prism-react-renderer: 2.4.1(react@19.2.3)
diff --git a/public/support-care-maven/component-logos/apache-commons.svg b/public/support-care-maven/component-logos/apache-commons.svg
new file mode 100644
index 00000000..3594bd62
--- /dev/null
+++ b/public/support-care-maven/component-logos/apache-commons.svg
@@ -0,0 +1,70 @@
+
+
+
diff --git a/public/support-care-maven/component-logos/apache-log4j.png b/public/support-care-maven/component-logos/apache-log4j.png
new file mode 100644
index 00000000..b6d34cec
Binary files /dev/null and b/public/support-care-maven/component-logos/apache-log4j.png differ
diff --git a/public/support-care-maven/component-logos/apache-maven.svg b/public/support-care-maven/component-logos/apache-maven.svg
new file mode 100644
index 00000000..24f2345a
--- /dev/null
+++ b/public/support-care-maven/component-logos/apache-maven.svg
@@ -0,0 +1,998 @@
+
+
+
+
diff --git a/public/support-care-maven/component-logos/eclipse-temurin.svg b/public/support-care-maven/component-logos/eclipse-temurin.svg
new file mode 100644
index 00000000..26cf42bc
--- /dev/null
+++ b/public/support-care-maven/component-logos/eclipse-temurin.svg
@@ -0,0 +1,35 @@
+
+
\ No newline at end of file
diff --git a/public/support-care-maven/component-logos/junit.svg b/public/support-care-maven/component-logos/junit.svg
new file mode 100644
index 00000000..c72cdbe8
--- /dev/null
+++ b/public/support-care-maven/component-logos/junit.svg
@@ -0,0 +1,34 @@
+
+
\ No newline at end of file
diff --git a/public/support-care-maven/diagram-1.png b/public/support-care-maven/diagram-1.png
deleted file mode 100644
index 643882f2..00000000
Binary files a/public/support-care-maven/diagram-1.png and /dev/null differ
diff --git a/public/support-care-maven/diagram.png b/public/support-care-maven/diagram.png
deleted file mode 100644
index 88a07d67..00000000
Binary files a/public/support-care-maven/diagram.png and /dev/null differ
diff --git a/public/support-care-maven/foundation-logos/afs.svg b/public/support-care-maven/foundation-logos/afs.svg
new file mode 100644
index 00000000..f1084dfb
--- /dev/null
+++ b/public/support-care-maven/foundation-logos/afs.svg
@@ -0,0 +1,43 @@
+
+
\ No newline at end of file
diff --git a/public/support-care-maven/foundation-logos/eclipse.png b/public/support-care-maven/foundation-logos/eclipse.png
new file mode 100644
index 00000000..37e6996d
Binary files /dev/null and b/public/support-care-maven/foundation-logos/eclipse.png differ
diff --git a/public/support-care-maven/foundation-logos/lf.svg b/public/support-care-maven/foundation-logos/lf.svg
new file mode 100644
index 00000000..e8df47f8
--- /dev/null
+++ b/public/support-care-maven/foundation-logos/lf.svg
@@ -0,0 +1,49 @@
+
+
+
diff --git a/public/support-care-landingpage/networking.png b/public/support-care-maven/networking.png
similarity index 100%
rename from public/support-care-landingpage/networking.png
rename to public/support-care-maven/networking.png
diff --git a/public/support-care-maven/oe-delivers-container.png b/public/support-care-maven/oe-delivers-container.png
new file mode 100644
index 00000000..2872524c
Binary files /dev/null and b/public/support-care-maven/oe-delivers-container.png differ
diff --git a/public/support-care-landingpage/os-benefits-de.png b/public/support-care-maven/os-benefits-de.png
similarity index 100%
rename from public/support-care-landingpage/os-benefits-de.png
rename to public/support-care-maven/os-benefits-de.png
diff --git a/public/support-care-landingpage/os-benefits.png b/public/support-care-maven/os-benefits.png
similarity index 100%
rename from public/support-care-landingpage/os-benefits.png
rename to public/support-care-maven/os-benefits.png
diff --git a/public/support-care-maven/pyramid.png b/public/support-care-maven/pyramid.png
new file mode 100644
index 00000000..c3aa3c77
Binary files /dev/null and b/public/support-care-maven/pyramid.png differ
diff --git a/public/support-care-maven/roadmap.png b/public/support-care-maven/roadmap.png
deleted file mode 100644
index 03bd6351..00000000
Binary files a/public/support-care-maven/roadmap.png and /dev/null differ
diff --git a/public/support-care-landingpage/roundtable.png b/public/support-care-maven/roundtable.png
similarity index 100%
rename from public/support-care-landingpage/roundtable.png
rename to public/support-care-maven/roundtable.png
diff --git a/public/support-care-maven/services-pictograms/custom-containers.svg b/public/support-care-maven/services-pictograms/custom-containers.svg
new file mode 100644
index 00000000..954177ac
--- /dev/null
+++ b/public/support-care-maven/services-pictograms/custom-containers.svg
@@ -0,0 +1,59 @@
+
+
\ No newline at end of file
diff --git a/public/support-care-maven/services-pictograms/documentation.svg b/public/support-care-maven/services-pictograms/documentation.svg
new file mode 100644
index 00000000..1443bca5
--- /dev/null
+++ b/public/support-care-maven/services-pictograms/documentation.svg
@@ -0,0 +1,87 @@
+
+
\ No newline at end of file
diff --git a/public/support-care-maven/services-pictograms/long-term-support.svg b/public/support-care-maven/services-pictograms/long-term-support.svg
new file mode 100755
index 00000000..f1e89fb9
--- /dev/null
+++ b/public/support-care-maven/services-pictograms/long-term-support.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/support-care-maven/services-pictograms/security.svg b/public/support-care-maven/services-pictograms/security.svg
new file mode 100755
index 00000000..3d0eb765
--- /dev/null
+++ b/public/support-care-maven/services-pictograms/security.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/support-care-maven/services-pictograms/workshops.svg b/public/support-care-maven/services-pictograms/workshops.svg
new file mode 100644
index 00000000..1d48cc04
--- /dev/null
+++ b/public/support-care-maven/services-pictograms/workshops.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/support-care-maven/support-and-care-logo.svg b/public/support-care-maven/support-and-care-logo.svg
new file mode 100644
index 00000000..831ad3cc
--- /dev/null
+++ b/public/support-care-maven/support-and-care-logo.svg
@@ -0,0 +1,62 @@
+
+
\ No newline at end of file
diff --git a/public/support-care-landingpage/support-care-logo.svg b/public/support-care-maven/support-care-logo.svg
similarity index 100%
rename from public/support-care-landingpage/support-care-logo.svg
rename to public/support-care-maven/support-care-logo.svg
diff --git a/public/support-care-landingpage/tree.png b/public/support-care-maven/tree.png
similarity index 100%
rename from public/support-care-landingpage/tree.png
rename to public/support-care-maven/tree.png
diff --git a/src/app/[locale]/[...rest]/page.tsx b/src/app/[locale]/[...rest]/page.tsx
index 322919b9..385e7e6a 100644
--- a/src/app/[locale]/[...rest]/page.tsx
+++ b/src/app/[locale]/[...rest]/page.tsx
@@ -27,7 +27,10 @@ function localePrefix(locale: string): string {
return locale === 'de' ? '/de' : '';
}
-function maybeRedirectLegacyPath(locale: string, requestPath: string): string | null {
+function maybeRedirectLegacyPath(
+ locale: string,
+ requestPath: string,
+): string | null {
const normalizedPath = requestPath.replace(/^\/+|\/+$/g, '');
const prefix = localePrefix(locale);
@@ -39,7 +42,11 @@ function maybeRedirectLegacyPath(locale: string, requestPath: string): string |
return `${prefix}/about`;
}
- if (REDIRECTS_TO_POSTS.has(normalizedPath) || normalizedPath.startsWith('categories/') || normalizedPath.startsWith('tags/')) {
+ if (
+ REDIRECTS_TO_POSTS.has(normalizedPath) ||
+ normalizedPath.startsWith('categories/') ||
+ normalizedPath.startsWith('tags/')
+ ) {
return `${prefix}/posts`;
}
@@ -62,7 +69,14 @@ function findMarkdownFile(locale: string, requestPath: string): string | null {
return null;
}
-async function loadPageData(locale: string, requestPath: string): Promise<{ title: string; description?: string; contentHtml: string } | null> {
+async function loadPageData(
+ locale: string,
+ requestPath: string,
+): Promise<{
+ title: string;
+ description?: string;
+ contentHtml: string;
+} | null> {
const markdownFile = findMarkdownFile(locale, requestPath);
if (!markdownFile) {
@@ -72,16 +86,22 @@ async function loadPageData(locale: string, requestPath: string): Promise<{ titl
const fileContents = fs.readFileSync(markdownFile, 'utf8');
const { data, content } = matter(fileContents);
const transformedContent = transformHugoShortcodes(content);
- const processedContent = await remark().use(remarkGfm).use(html, { sanitize: false }).process(transformedContent);
+ const processedContent = await remark()
+ .use(remarkGfm)
+ .use(html, { sanitize: false })
+ .process(transformedContent);
return {
title: typeof data.title === 'string' ? data.title : 'Open Elements',
- description: typeof data.description === 'string' ? data.description : undefined,
+ description:
+ typeof data.description === 'string' ? data.description : undefined,
contentHtml: processedContent.toString(),
};
}
-export async function generateMetadata({ params }: CatchAllPageProps): Promise
- {bulletPoints.map((bulletPoint) => (
+ {bulletPoints.map(bulletPoint => (
@@ -113,8 +118,7 @@ export default async function DltLecturePage({ params }: DltLecturePageProps) {
viewBox="0 0 64 56"
fill="none"
xmlns="http://www.w3.org/2000/svg"
- aria-hidden="true"
- >
+ aria-hidden="true">
Responsible for the content according to § 55 paragraph 2 RStV: Hendrik Ebbers, Gerhart-Hauptmann-Str. 49B, 51379 Leverkusen
([\s\S]*?)<\/code><\/pre>/gi;
+const CODE_BLOCK_PATTERN =
+ /([\s\S]*?)<\/code><\/pre>/gi;
const PRISM_TOKEN_COLORS = new Map([
['comment', '#75715e'],
['prolog', '#75715e'],
@@ -93,9 +94,9 @@ loadLanguages([
'xml',
]);
-Prism.hooks.add('wrap', (environment) => {
+Prism.hooks.add('wrap', environment => {
const tokenClass = environment.classes.find(
- (className) => className !== 'token' && PRISM_TOKEN_COLORS.has(className),
+ className => className !== 'token' && PRISM_TOKEN_COLORS.has(className),
);
if (!tokenClass) {
@@ -158,7 +159,10 @@ function normalizeLegacySlugSegment(slug: string): string {
.replace(/^-+|-+$/g, '');
}
-function isDateBasedSlugMatch(candidatePath: string, requestedPath: string): boolean {
+function isDateBasedSlugMatch(
+ candidatePath: string,
+ requestedPath: string,
+): boolean {
if (candidatePath === requestedPath) {
return true;
}
@@ -177,13 +181,16 @@ function isDateBasedSlugMatch(candidatePath: string, requestedPath: string): boo
return false;
}
- return normalizeLegacySlugSegment(candidateSegments[3]) === normalizeLegacySlugSegment(requestedSegments[3]);
+ return (
+ normalizeLegacySlugSegment(candidateSegments[3]) ===
+ normalizeLegacySlugSegment(requestedSegments[3])
+ );
}
function decodePathSegments(pathValue: string): string {
return pathValue
.split('/')
- .map((segment) => {
+ .map(segment => {
try {
return decodeURIComponent(segment);
} catch {
@@ -232,7 +239,12 @@ function getPostFilename(slugPath: string, locale: string): string | null {
const fileContents = fs.readFileSync(fullPath, 'utf8');
const { data } = matter(fileContents);
- if (isDateBasedSlugMatch(generatePostPath(data as PostFrontmatter), decodedSlugPath)) {
+ if (
+ isDateBasedSlugMatch(
+ generatePostPath(data as PostFrontmatter),
+ decodedSlugPath,
+ )
+ ) {
return filename;
}
}
@@ -272,7 +284,10 @@ export function getPostLocaleAlternates(
if (files.includes(enFile)) {
const fullPath = path.join(postsDirectory, enFile);
const { data } = matter(fs.readFileSync(fullPath, 'utf8'));
- alternates.push({ locale: 'en', slugPath: generatePostPath(data as PostFrontmatter) });
+ alternates.push({
+ locale: 'en',
+ slugPath: generatePostPath(data as PostFrontmatter),
+ });
}
// Check German version
@@ -280,7 +295,10 @@ export function getPostLocaleAlternates(
if (files.includes(deFile)) {
const fullPath = path.join(postsDirectory, deFile);
const { data } = matter(fs.readFileSync(fullPath, 'utf8'));
- alternates.push({ locale: 'de', slugPath: generatePostPath(data as PostFrontmatter) });
+ alternates.push({
+ locale: 'de',
+ slugPath: generatePostPath(data as PostFrontmatter),
+ });
}
return alternates;
@@ -327,12 +345,15 @@ function ensureSafeRel(attributes: string): string {
const relPattern = /\brel\s*=\s*(["'])(.*?)\1/i;
if (relPattern.test(attributes)) {
- return attributes.replace(relPattern, (_match, quote: string, relValue: string) => {
- const tokens = new Set(relValue.split(/\s+/).filter(Boolean));
- tokens.add('noopener');
- tokens.add('noreferrer');
- return `rel=${quote}${Array.from(tokens).join(' ')}${quote}`;
- });
+ return attributes.replace(
+ relPattern,
+ (_match, quote: string, relValue: string) => {
+ const tokens = new Set(relValue.split(/\s+/).filter(Boolean));
+ tokens.add('noopener');
+ tokens.add('noreferrer');
+ return `rel=${quote}${Array.from(tokens).join(' ')}${quote}`;
+ },
+ );
}
return `${attributes} rel="noopener noreferrer"`;
@@ -439,18 +460,23 @@ function extractHeadingText(headingHtml: string): string {
function appendHtmlAttribute(attributes: string, attribute: string): string {
const normalizedAttributes = attributes.trimEnd();
- return normalizedAttributes ? `${normalizedAttributes} ${attribute}` : ` ${attribute}`;
+ return normalizedAttributes
+ ? `${normalizedAttributes} ${attribute}`
+ : ` ${attribute}`;
}
function ensureHtmlClass(attributes: string, className: string): string {
const classPattern = /\bclass\s*=\s*(["'])(.*?)\1/i;
if (classPattern.test(attributes)) {
- return attributes.replace(classPattern, (_match, quote: string, classValue: string) => {
- const classes = new Set(classValue.split(/\s+/).filter(Boolean));
- classes.add(className);
- return `class=${quote}${Array.from(classes).join(' ')}${quote}`;
- });
+ return attributes.replace(
+ classPattern,
+ (_match, quote: string, classValue: string) => {
+ const classes = new Set(classValue.split(/\s+/).filter(Boolean));
+ classes.add(className);
+ return `class=${quote}${Array.from(classes).join(' ')}${quote}`;
+ },
+ );
}
return appendHtmlAttribute(attributes, `class="${className}"`);
@@ -480,7 +506,10 @@ function decorateHeadlinesWithAnchors(contentHtml: string): string {
let nextAttributes = attributes;
if (!existingIdMatch) {
- nextAttributes = appendHtmlAttribute(nextAttributes, `id="${headingId}"`);
+ nextAttributes = appendHtmlAttribute(
+ nextAttributes,
+ `id="${headingId}"`,
+ );
}
nextAttributes = ensureHtmlClass(nextAttributes, 'scroll-mt-10');
@@ -518,7 +547,10 @@ function normalizeCodeLanguage(language: string): string | null {
];
for (const [prefix, mappedLanguage] of aliases) {
- if (normalizedLanguage === prefix || normalizedLanguage.startsWith(prefix)) {
+ if (
+ normalizedLanguage === prefix ||
+ normalizedLanguage.startsWith(prefix)
+ ) {
return mappedLanguage;
}
}
@@ -565,7 +597,10 @@ function highlightCodeBlocks(contentHtml: string): string {
* By default, posts with showInBlog === false are excluded (for the blog listing UI).
* Pass includeHidden = true to include all posts (e.g. for sitemap generation).
*/
-export function getAllPosts(locale: string, { includeHidden = false }: { includeHidden?: boolean } = {}): PostData[] {
+export function getAllPosts(
+ locale: string,
+ { includeHidden = false }: { includeHidden?: boolean } = {},
+): PostData[] {
const files = fs.readdirSync(postsDirectory);
const posts: PostData[] = [];
@@ -680,7 +715,9 @@ export async function getPostBySlug(
.process(transformedContent);
const contentHtml = highlightCodeBlocks(
decorateHeadlinesWithAnchors(
- decorateExternalLinks(centerStandaloneHtmlImages(processedContent.toString())),
+ decorateExternalLinks(
+ centerStandaloneHtmlImages(processedContent.toString()),
+ ),
),
);
@@ -691,7 +728,10 @@ export async function getPostBySlug(
contentHtml,
};
} catch (error) {
- console.error(`Error fetching post ${slugPath} for locale ${locale}:`, error);
+ console.error(
+ `Error fetching post ${slugPath} for locale ${locale}:`,
+ error,
+ );
return null;
}
}
diff --git a/src/lib/remark-hugo-shortcodes.ts b/src/lib/remark-hugo-shortcodes.ts
index 97984869..145e5c59 100644
--- a/src/lib/remark-hugo-shortcodes.ts
+++ b/src/lib/remark-hugo-shortcodes.ts
@@ -55,31 +55,34 @@ class ShortcodeRegistry {
*/
private registerDefaultHandlers(): void {
// Centered image shortcode
- this.register('centered-image', (attrs) => {
+ this.register('centered-image', attrs => {
const src = attrs.src || '';
const alt = this.escapeHtml(attrs.alt || '');
const width = attrs.width || '100%';
const showCaption = attrs.showCaption === 'true';
-
+
const imageHtml = `
`;
- const captionHtml = showCaption && alt ? `${alt}
` : '';
-
+ const captionHtml =
+ showCaption && alt
+ ? `${alt}
`
+ : '';
+
return `${imageHtml}${captionHtml}`;
});
// Relative reference shortcode
- this.register('relref', (attrs) => {
+ this.register('relref', attrs => {
// Extract path from first attribute or 'path' key
const path = attrs._content || attrs.path || '';
return `/${path.replace(/^\/+/, '')}`;
});
- this.register('ref', (attrs) => {
+ this.register('ref', attrs => {
const path = attrs._content || attrs.path || '';
return `/${path.replace(/^\/+/, '')}`;
});
- this.register('youtube', (attrs) => {
+ this.register('youtube', attrs => {
const videoId = this.escapeHtml(attrs._content || attrs.id || '');
if (!videoId) {
@@ -107,7 +110,7 @@ class ShortcodeRegistry {
'"': '"',
"'": ''',
};
- return text.replace(/[&<>"']/g, (char) => escapeMap[char] || char);
+ return text.replace(/[&<>"']/g, char => escapeMap[char] || char);
}
}
@@ -127,7 +130,10 @@ function replaceShortcodes(text: string, registry: ShortcodeRegistry): string {
if (handler) {
const replacement = handler(shortcode.attributes);
- result = result.slice(0, shortcode.startIndex) + replacement + result.slice(shortcode.endIndex);
+ result =
+ result.slice(0, shortcode.startIndex) +
+ replacement +
+ result.slice(shortcode.endIndex);
}
}
@@ -142,8 +148,10 @@ export function transformHugoShortcodes(text: string): string {
* Parser for Hugo shortcodes
*/
class ShortcodeParser {
- private static readonly SHORTCODE_PATTERN = /\{\{<\s*([a-z-]+)\s+([^>]*?)\s*>\}\}/g;
- private static readonly ATTRIBUTE_PATTERN = /(\w+)=(?:"([^"]*)"|([^\s"]+))|"([^"]*)"|([^\s"]+)/g;
+ private static readonly SHORTCODE_PATTERN =
+ /\{\{<\s*([a-z-]+)\s+([^>]*?)\s*>\}\}/g;
+ private static readonly ATTRIBUTE_PATTERN =
+ /(\w+)=(?:"([^"]*)"|([^\s"]+))|"([^"]*)"|([^\s"]+)/g;
/**
* Parse all shortcodes in a text string
@@ -213,21 +221,25 @@ export const remarkHugoShortcodes: Plugin<[], Root> = () => {
const registry = new ShortcodeRegistry();
return (tree: Root) => {
- visit(tree, 'text', (node: Text, index: number | undefined, parent: Parent | undefined) => {
- if (!node.value || typeof index !== 'number' || !parent) {
- return;
- }
+ visit(
+ tree,
+ 'text',
+ (node: Text, index: number | undefined, parent: Parent | undefined) => {
+ if (!node.value || typeof index !== 'number' || !parent) {
+ return;
+ }
- const result = replaceShortcodes(node.value, registry);
+ const result = replaceShortcodes(node.value, registry);
- // Replace text node with HTML node if changes were made
- if (result !== node.value) {
- const htmlNode: Html = {
- type: 'html',
- value: result,
- };
- parent.children[index] = htmlNode;
- }
- });
+ // Replace text node with HTML node if changes were made
+ if (result !== node.value) {
+ const htmlNode: Html = {
+ type: 'html',
+ value: result,
+ };
+ parent.children[index] = htmlNode;
+ }
+ },
+ );
};
};
diff --git a/src/lib/updates.ts b/src/lib/updates.ts
index 9ebb64b7..8719bebd 100644
--- a/src/lib/updates.ts
+++ b/src/lib/updates.ts
@@ -39,7 +39,9 @@ function parseMarkdownSections(content: string): {
}
const title = line.substring(3).trim();
- isOverview = title.toLowerCase() === 'overview' || title.toLowerCase() === 'überblick';
+ isOverview =
+ title.toLowerCase() === 'overview' ||
+ title.toLowerCase() === 'überblick';
if (!isOverview) {
// Start new section
@@ -57,8 +59,8 @@ function parseMarkdownSections(content: string): {
const comment = line.trim().slice(4, -3).trim();
if (currentSection) {
// Parse metadata from comment
- const parts = comment.split(',').map((p) => p.trim());
- parts.forEach((part) => {
+ const parts = comment.split(',').map(p => p.trim());
+ parts.forEach(part => {
if (!currentSection) return;
if (part === 'collapsible') {
currentSection.metadata.collapsible = true;
@@ -87,12 +89,12 @@ function parseMarkdownSections(content: string): {
}
// Convert raw sections to UpdateSection format
- const sections: UpdateSection[] = rawSections.map((rawSection) => {
+ const sections: UpdateSection[] = rawSections.map(rawSection => {
// Parse list items from content
const items = rawSection.content
.split('\n')
- .filter((line) => line.trim().startsWith('-'))
- .map((line) => ({
+ .filter(line => line.trim().startsWith('-'))
+ .map(line => ({
text: line.trim().substring(1).trim(),
}));
@@ -128,7 +130,7 @@ export function getUpdateFiles(locale: string): string[] {
const files = fs.readdirSync(updatesDirectory);
return files
- .filter((file) => file.endsWith(`.${locale}.md`))
+ .filter(file => file.endsWith(`.${locale}.md`))
.sort()
.reverse(); // Most recent first
}
@@ -136,7 +138,10 @@ export function getUpdateFiles(locale: string): string[] {
/**
* Parse a single update file
*/
-export function parseUpdateFile(filename: string, locale: string): Update | null {
+export function parseUpdateFile(
+ filename: string,
+ locale: string,
+): Update | null {
const fullPath = path.join(updatesDirectory, filename);
if (!fs.existsSync(fullPath)) {
@@ -199,7 +204,10 @@ export function getAllUpdates(locale: string): Update[] {
/**
* Get a single update by version
*/
-export function getUpdateByVersion(version: string, locale: string): Update | null {
+export function getUpdateByVersion(
+ version: string,
+ locale: string,
+): Update | null {
const filename = `${version}.${locale}.md`;
return parseUpdateFile(filename, locale);
}
diff --git a/src/middleware.ts b/src/middleware.ts
index 289a7469..dd8e49c7 100644
--- a/src/middleware.ts
+++ b/src/middleware.ts
@@ -2,12 +2,16 @@ import createMiddleware from 'next-intl/middleware';
import { routing } from './i18n/routing';
const intlMiddleware = createMiddleware(routing);
-const STATIC_FILE_EXTENSION_PATTERN = /\.(?:avif|bmp|css|gif|ico|jpeg|jpg|js|json|map|mp4|png|svg|txt|webm|webp|woff|woff2|xml)$/i;
+const STATIC_FILE_EXTENSION_PATTERN =
+ /\.(?:avif|bmp|css|gif|ico|jpeg|jpg|js|json|map|mp4|png|svg|txt|webm|webp|woff|woff2|xml)$/i;
export default function middleware(req: any) {
const { pathname } = req.nextUrl;
- if (pathname.endsWith('sitemap.xml') || STATIC_FILE_EXTENSION_PATTERN.test(pathname)) {
+ if (
+ pathname.endsWith('sitemap.xml') ||
+ STATIC_FILE_EXTENSION_PATTERN.test(pathname)
+ ) {
return;
}
return intlMiddleware(req);
@@ -17,5 +21,10 @@ export const config = {
// Match all pathnames except for
// - ... if they start with `/api`, `/_next` or `/_vercel`
// - ... if they contain a dot (e.g. `favicon.ico`)
- matcher: ['/', '/posts/:path*', '/(de|en)/:path*', '/((?!api|_next|_vercel|.*\\..*).*)'],
+ matcher: [
+ '/',
+ '/posts/:path*',
+ '/(de|en)/:path*',
+ '/((?!api|_next|_vercel|.*\\..*).*)',
+ ],
};
diff --git a/src/next-env.d.ts b/src/next-env.d.ts
deleted file mode 100644
index 1b3be084..00000000
--- a/src/next-env.d.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-///
-///
-
-// NOTE: This file should not be edited
-// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/src/types/css.d.ts b/src/types/css.d.ts
index 3255cfe2..35306c6f 100644
--- a/src/types/css.d.ts
+++ b/src/types/css.d.ts
@@ -1 +1 @@
-declare module '*.css';
\ No newline at end of file
+declare module '*.css';