diff --git a/apps/web/app/api/download-zip/route.ts b/apps/web/app/api/download-zip/route.ts deleted file mode 100644 index ceee91d..0000000 --- a/apps/web/app/api/download-zip/route.ts +++ /dev/null @@ -1,268 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { FileExampleManager } from '../../../src/data/fileExamples'; - -// Simple ZIP creation without external dependencies -class SimpleZipCreator { - private files: Array<{ filename: string; content: string }> = []; - - addFile(filename: string, content: string) { - this.files.push({ filename, content }); - } - - // Create a basic ZIP file structure - create(): number[] { - const encoder = new TextEncoder(); - const chunks: number[][] = []; - - let offset = 0; - const centralDirectory: Array<{ - name: string; - offset: number; - size: number; - compressedSize: number; - }> = []; - - // Process each file - for (const file of this.files) { - const contentBytes = Array.from(encoder.encode(file.content)); - const filenameBytes = Array.from(encoder.encode(file.filename)); - - // Local file header - const localHeader: number[] = []; - - // Local file header signature (little endian) - localHeader.push(0x50, 0x4b, 0x03, 0x04); - // Version needed to extract - localHeader.push(20, 0); - // General purpose bit flag - localHeader.push(0, 0); - // Compression method (0 = store) - localHeader.push(0, 0); - // Last modified time/date - localHeader.push(0, 0, 0, 0); - // CRC32 (0 for simplicity) - localHeader.push(0, 0, 0, 0); - // Compressed size - localHeader.push(...intToLittleEndian(contentBytes.length, 4)); - // Uncompressed size - localHeader.push(...intToLittleEndian(contentBytes.length, 4)); - // Filename length - localHeader.push(...intToLittleEndian(filenameBytes.length, 2)); - // Extra field length - localHeader.push(0, 0); - - // Add filename - localHeader.push(...filenameBytes); - - chunks.push(localHeader); - chunks.push(contentBytes); - - // Store info for central directory - centralDirectory.push({ - name: file.filename, - offset: offset, - size: contentBytes.length, - compressedSize: contentBytes.length - }); - - offset += localHeader.length + contentBytes.length; - } - - // Central directory - const centralDirectoryChunks: number[][] = []; - let centralDirectoryOffset = offset; - - for (const entry of centralDirectory) { - const filenameBytes = Array.from(encoder.encode(entry.name)); - const centralHeader: number[] = []; - - // Central directory header signature - centralHeader.push(0x50, 0x4b, 0x01, 0x02); - // Version made by - centralHeader.push(20, 0); - // Version needed to extract - centralHeader.push(20, 0); - // General purpose bit flag - centralHeader.push(0, 0); - // Compression method - centralHeader.push(0, 0); - // Last modified time/date - centralHeader.push(0, 0, 0, 0); - // CRC32 - centralHeader.push(0, 0, 0, 0); - // Compressed size - centralHeader.push(...intToLittleEndian(entry.compressedSize, 4)); - // Uncompressed size - centralHeader.push(...intToLittleEndian(entry.size, 4)); - // Filename length - centralHeader.push(...intToLittleEndian(filenameBytes.length, 2)); - // Extra field length - centralHeader.push(0, 0); - // File comment length - centralHeader.push(0, 0); - // Disk number start - centralHeader.push(0, 0); - // Internal file attributes - centralHeader.push(0, 0); - // External file attributes - centralHeader.push(0, 0, 0, 0); - // Relative offset of local header - centralHeader.push(...intToLittleEndian(entry.offset, 4)); - - // Add filename - centralHeader.push(...filenameBytes); - - centralDirectoryChunks.push(centralHeader); - } - - const centralDirectorySize = centralDirectoryChunks.reduce((sum, chunk) => sum + chunk.length, 0); - - // End of central directory - const endOfCentralDirectory: number[] = []; - - // End of central directory signature - endOfCentralDirectory.push(0x50, 0x4b, 0x05, 0x06); - // Number of this disk - endOfCentralDirectory.push(0, 0); - // Number of the disk with the start of the central directory - endOfCentralDirectory.push(0, 0); - // Total number of entries on this disk - endOfCentralDirectory.push(...intToLittleEndian(centralDirectory.length, 2)); - // Total number of entries - endOfCentralDirectory.push(...intToLittleEndian(centralDirectory.length, 2)); - // Size of the central directory - endOfCentralDirectory.push(...intToLittleEndian(centralDirectorySize, 4)); - // Offset of start of central directory - endOfCentralDirectory.push(...intToLittleEndian(centralDirectoryOffset, 4)); - // ZIP file comment length - endOfCentralDirectory.push(0, 0); - - // Combine all chunks - const result: number[] = []; - chunks.forEach(chunk => result.push(...chunk)); - centralDirectoryChunks.forEach(chunk => result.push(...chunk)); - result.push(...endOfCentralDirectory); - - return result; - } -} - -// Helper function to convert integer to little endian bytes -function intToLittleEndian(value: number, bytes: number): number[] { - const result: number[] = []; - for (let i = 0; i < bytes; i++) { - result.push((value >> (i * 8)) & 0xff); - } - return result; -} - -export async function POST(request: NextRequest) { - try { - const body = await request.json(); - const { fileIds } = body; - - if (!Array.isArray(fileIds) || fileIds.length === 0) { - return NextResponse.json( - { error: 'fileIds array is required and must not be empty' }, - { status: 400 } - ); - } - - // Get file contents - const files = await Promise.all( - fileIds.map(async (id) => { - const file = await FileExampleManager.getFileExample(id); - if (!file) { - throw new Error(`File with id ${id} not found`); - } - return file; - }) - ); - - // Create ZIP - const zipCreator = new SimpleZipCreator(); - files.forEach(file => { - zipCreator.addFile(file.filename, file.content); - }); - - const zipData = zipCreator.create(); - const buffer = Buffer.from(new Uint8Array(zipData)); - - // Return ZIP file - return new Response(buffer, { - headers: { - 'Content-Type': 'application/zip', - 'Content-Disposition': `attachment; filename="code-examples-${Date.now()}.zip"`, - 'Cache-Control': 'no-cache', - } - }); - - } catch (error) { - console.error('ZIP download error:', error); - const errorMessage = error instanceof Error ? error.message : 'Unknown error'; - return NextResponse.json( - { error: 'Failed to create zip file', details: errorMessage }, - { status: 500 } - ); - } -} - -export async function GET(request: NextRequest) { - try { - const { searchParams } = new URL(request.url); - const fileId = searchParams.get('id'); - - if (!fileId) { - return NextResponse.json( - { error: 'id parameter is required' }, - { status: 400 } - ); - } - - const file = await FileExampleManager.getFileExample(fileId); - - if (!file) { - return NextResponse.json( - { error: 'File not found' }, - { status: 404 } - ); - } - - const encoder = new TextEncoder(); - const content = encoder.encode(file.content); - const buffer = Buffer.from(content); - - return new Response(buffer, { - headers: { - 'Content-Type': getMimeType(file.language), - 'Content-Disposition': `attachment; filename="${file.filename}"`, - 'Cache-Control': 'no-cache', - } - }); - - } catch (error) { - console.error('File download error:', error); - return NextResponse.json( - { error: 'Failed to download file' }, - { status: 500 } - ); - } -} - -// Helper function to get MIME type -function getMimeType(language: string): string { - const mimeTypes: Record = { - 'python': 'text/x-python', - 'typescript': 'text/x-typescript', - 'javascript': 'text/javascript', - 'dockerfile': 'text/x-dockerfile', - 'yaml': 'text/yaml', - 'json': 'application/json', - 'html': 'text/html', - 'css': 'text/css', - 'sql': 'text/x-sql', - 'bash': 'text/x-shellscript', - 'text': 'text/plain' - }; - return mimeTypes[language] || 'text/plain'; -} diff --git a/apps/web/app/blog/[slug]/opengraph-image.tsx b/apps/web/app/blog/[slug]/opengraph-image.tsx index 6cd959d..ddbaf54 100644 --- a/apps/web/app/blog/[slug]/opengraph-image.tsx +++ b/apps/web/app/blog/[slug]/opengraph-image.tsx @@ -1,6 +1,6 @@ import { ImageResponse } from "next/og"; -import { blogPosts } from "../../../src/data/blogPosts"; -import { blogThumbnails } from "../../../src/data/blogThumbnails"; +import { allPosts } from "contentlayer/generated"; +import { blogThumbnails } from "../../../src/components/blog/blogThumbnails"; import { OGImageTemplate } from "../../../src/components/OGImageTemplate"; import { getOgFonts, OG_IMAGE_SIZE } from "../../../src/lib/og-helper"; @@ -14,7 +14,7 @@ export default async function Image({ params: Promise<{ slug: string }>; }) { const { slug } = await params; - const post = blogPosts.find((p) => p.slug === slug); + const post = allPosts.find((p) => p.slug === slug); const thumbnail = blogThumbnails[slug]; const title = post?.title || "Marc Mintel"; diff --git a/apps/web/app/blog/[slug]/page.tsx b/apps/web/app/blog/[slug]/page.tsx index c9cb5d2..a39695e 100644 --- a/apps/web/app/blog/[slug]/page.tsx +++ b/apps/web/app/blog/[slug]/page.tsx @@ -1,17 +1,18 @@ import * as React from "react"; import type { Metadata } from "next"; import { notFound } from "next/navigation"; -import { blogPosts } from "../../../src/data/blogPosts"; +import { allPosts } from "contentlayer/generated"; import { BlogPostHeader } from "../../../src/components/blog/BlogPostHeader"; import { Section } from "../../../src/components/Section"; import { Reveal } from "../../../src/components/Reveal"; import { BlogPostClient } from "../../../src/components/BlogPostClient"; -import { PostComponents } from "../../../src/components/blog/posts"; import { TextSelectionShare } from "../../../src/components/TextSelectionShare"; import { BlogPostStickyBar } from "../../../src/components/blog/BlogPostStickyBar"; +import { MDXRemote } from "next-mdx-remote/rsc"; +import { MDXComponents } from "../../../mdx-components"; export async function generateStaticParams() { - return blogPosts.map((post) => ({ + return allPosts.map((post) => ({ slug: post.slug, })); } @@ -22,7 +23,7 @@ export async function generateMetadata({ params: Promise<{ slug: string }>; }): Promise { const { slug } = await params; - const post = blogPosts.find((p) => p.slug === slug); + const post = allPosts.find((p) => p.slug === slug); if (!post) return {}; @@ -48,7 +49,7 @@ export default async function BlogPostPage({ params: Promise<{ slug: string }>; }) { const { slug } = await params; - const post = blogPosts.find((p) => p.slug === slug); + const post = allPosts.find((p) => p.slug === slug); if (!post) { notFound(); @@ -60,11 +61,9 @@ export default async function BlogPostPage({ year: "numeric", }); - const wordCount = post.description.split(/\s+/).length + 300; // Average post length + const wordCount = post.description.split(/\s+/).length + 300; const readingTime = Math.max(1, Math.ceil(wordCount / 200)); - const PostContent = PostComponents[slug]; - return (
@@ -78,7 +77,6 @@ export default async function BlogPostPage({ />
- {/* Sticky Progress Bar */} {post.tags && post.tags.length > 0 && (
- {post.tags.map((tag, index) => ( + {post.tags?.map((tag) => ( )} - {PostContent ? ( - - ) : ( -
- Inhalt wird bald veröffentlicht... -
- )} +
+ +
diff --git a/apps/web/app/blog/page.tsx b/apps/web/app/blog/page.tsx index 0be66fd..4401bf6 100644 --- a/apps/web/app/blog/page.tsx +++ b/apps/web/app/blog/page.tsx @@ -4,7 +4,7 @@ import * as React from "react"; import { useState, useEffect } from "react"; import { MediumCard } from "../../src/components/MediumCard"; import { BlogCommandBar } from "../../src/components/blog/BlogCommandBar"; -import { blogPosts } from "../../src/data/blogPosts"; +import { allPosts as contentPosts } from "contentlayer/generated"; import { SectionHeader } from "../../src/components/SectionHeader"; import { Reveal } from "../../src/components/Reveal"; import { Section } from "../../src/components/Section"; @@ -19,7 +19,7 @@ export default function BlogPage() { // Memoize allPosts const allPosts = React.useMemo( () => - [...blogPosts].sort( + [...contentPosts].sort( (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime(), ), [], diff --git a/apps/web/app/sitemap.ts b/apps/web/app/sitemap.ts index d53e1a4..b42d8f5 100644 --- a/apps/web/app/sitemap.ts +++ b/apps/web/app/sitemap.ts @@ -1,5 +1,5 @@ import { MetadataRoute } from 'next'; -import { blogPosts } from '../src/data/blogPosts'; +import { allPosts } from 'contentlayer/generated'; import { technologies } from './technologies/[slug]/data'; /** @@ -28,7 +28,7 @@ export default function sitemap(): MetadataRoute.Sitemap { })); // 2. Dynamic Blog Posts - const blogRoutes = blogPosts.map((post) => ({ + const blogRoutes = allPosts.map((post) => ({ url: `${baseUrl}/blog/${post.slug}`, lastModified: new Date(post.date), changeFrequency: 'monthly' as const, @@ -44,7 +44,7 @@ export default function sitemap(): MetadataRoute.Sitemap { })); // 4. Tag Pages - const allTags = [...new Set(blogPosts.flatMap((post) => post.tags))]; + const allTags = [...new Set(allPosts.flatMap((post) => post.tags))]; const tagRoutes = allTags.map((tag) => ({ url: `${baseUrl}/tags/${tag}`, lastModified: new Date(), diff --git a/apps/web/app/tags/[tag]/page.tsx b/apps/web/app/tags/[tag]/page.tsx index 375f352..adc066e 100644 --- a/apps/web/app/tags/[tag]/page.tsx +++ b/apps/web/app/tags/[tag]/page.tsx @@ -1,12 +1,11 @@ -import * as React from "react"; import Link from "next/link"; -import { blogPosts } from "../../../src/data/blogPosts"; +import { allPosts } from "contentlayer/generated"; import { MediumCard } from "../../../src/components/MediumCard"; import { Reveal } from "../../../src/components/Reveal"; export async function generateStaticParams() { const allTags = Array.from( - new Set(blogPosts.flatMap((post) => post.tags || [])), + new Set(allPosts.flatMap((post) => post.tags || [])), ); return allTags.map((tag) => ({ tag, @@ -19,7 +18,7 @@ export default async function TagPage({ params: Promise<{ tag: string }>; }) { const { tag } = await params; - const posts = blogPosts.filter((post) => post.tags?.includes(tag)); + const posts = allPosts.filter((post) => post.tags?.includes(tag)); return (
diff --git a/apps/web/content-engine.config.ts b/apps/web/content-engine.config.ts new file mode 100644 index 0000000..06991b6 --- /dev/null +++ b/apps/web/content-engine.config.ts @@ -0,0 +1,11 @@ +import { ComponentDefinition } from '@mintel/content-engine'; +import path from 'path'; +import { componentDefinitions } from './src/content-engine/definitions'; + +export const config = { + // Path to documentation files used as context for the AI + contextDir: path.join(process.cwd(), 'docs'), + + // Custom UI components available for injection + components: componentDefinitions +}; diff --git a/apps/web/content/blog/analytics-without-tracking.mdx b/apps/web/content/blog/analytics-without-tracking.mdx new file mode 100644 index 0000000..e44d414 --- /dev/null +++ b/apps/web/content/blog/analytics-without-tracking.mdx @@ -0,0 +1,150 @@ +--- +title: "Analytics ohne Tracking: Erkenntnis ohne Überwachung" +description: "Wie Sie Besucherströme präzise messen, ohne die Privatsphäre Ihrer Kunden zu verletzen." +date: "2026-02-09" +tags: ["privacy", "analytics"] +--- + + + "Ich brauche Google Analytics, um zu wissen, was meine Nutzer tun." – Das + ist eine weit verbreitete Fehlannahme. + + + In meiner täglichen Arbeit als Digital Architect beweise ich das + Gegenteil:{" "} + Maximale Erkenntnis erfordert keine maximale Überwachung. + + + Ich zeige Ihnen, wie wir Erfolg präzise messen, ohne die Privatsphäre + Ihrer Kunden an US-Konzerne zu verkaufen. + + +

Analytics ohne den Beigeschmack der Überwachung

+ + Klassische Analytics-Tools funktionieren wie ein Trojaner. + + + Sie sammeln riesige Mengen an persönlichen Daten, um daraus Profile zu + bilden, die weit über Ihre Website hinausgehen. + + + Dies zwingt Sie rechtlich in die Knie – Sie brauchen Banner, Consent-Tools + und riskieren Abmahnungen. + + + Das Paradoxon:{" "} + Die meisten dieser Daten brauchen Sie gar nicht. + + + Sie wollen wissen, welche Inhalte funktionieren, nicht wie der Nutzer in + seiner Freizeit heißt. + + +
+ +graph LR + Traffic["Besucherstrom"] --> Filter["Privacy-Proxy"] + Filter --> Metrics["Aggregate Metriken (Trends)"] + Filter --> Zero["Zero PII (No Personal Info)"] + Metrics --> Insights["Optimierung Ihres Business"] + Zero --> Compliance["100% DSGVO & Banner-Frei"] + style Insights fill:#4ade80,stroke:#333 + style Compliance fill:#4ade80,stroke:#333 + +
+ Ethisches Tracking: Wir gewinnen wertvolle Business-Insights, während + die Identität der Nutzer absolut geschützt bleibt. +
+
+ +

Qualität der Daten vor Quantität der Profile

+ + Mein Ansatz basiert auf aggregierten Trends statt auf individueller + Verfolgung. + + + Wir messen Seitenaufrufe, Verweildauer und Conversions – aber wir tun es{" "} + anonym und am Edge. + + + Das Ergebnis ist statistisch genauso wertvoll für Ihr Marketing, aber + moralisch und rechtlich weit überlegen. + + + Wahre Souveränität bedeutet, Insights zu generieren, ohne sich von + GA4-Komplexität abhängig zu machen. + + +

Meine Architektur für ethische Insights

+Ich integriere Analytics direkt in Ihre Plattform. + + Keine externen Scripte bedeutet auch: Mehr Performance für Ihre Nutzer. + + + + + Cookieless Tracking: Wir erkennen wiederkehrende Nutzer + über kurzlebige, anonyme Hashes. Keine Speicherung am Endgerät nötig. + + + First-Party Data: Die Daten bleiben auf Ihrem Server. + Kein Abfluss an Drittanbieter-Netzwerke zur Werbeoptimierung. + + + Lightweight Implementation: Statt 100KB + Analytics-Ballast nutzen wir Lösungen, die weniger als 1KB wiegen.{" "} + Geschwindigkeit trifft auf Erkenntnis. + + + +
+ +
+ +

Der unternehmerische Hebel: Banner-freie Leads

+ + Wenn Sie kein Banner brauchen, messen Sie 100 % Ihres Traffics. + + + Bei Google Analytics verlieren Sie oft 40-60 % der Daten, weil Nutzer den + Consent ablehnen. + + + Mein System liefert Ihnen die echten Zahlen, weil die + Hürde der Zustimmung technisch entfällt. + + + Ehrlichkeit zahlt sich hier direkt in der Genauigkeit Ihrer + Marketing-Planung aus. + + + Gute Entscheidungen brauchen ein solides Fundament – keine lückenhaften + Statistiken. + + +

Wann sollten Sie umstellen?

+Haben Sie genug von rechtlichen Grauzonen? + + Ich baue für Entscheider, die{" "} + Datenschutz als Teil ihres Markenversprechens begreifen. + + + Lassen wir Analytics von einer Last zu einer Stärke machen. + + +

Fazit: Wissen ist Macht, Respekt ist Zukunft

+Messen Sie, was zählt – und schützen Sie, wer zählt. + + Ich begleite Sie bei der Installation einer Lösung, die Professionalität + und Ethik brillant vereint. + + + Insights ohne Reue. Das ist modernes digitales + Management. + diff --git a/apps/web/content/blog/build-first-digital-architecture.mdx b/apps/web/content/blog/build-first-digital-architecture.mdx new file mode 100644 index 0000000..0606a16 --- /dev/null +++ b/apps/web/content/blog/build-first-digital-architecture.mdx @@ -0,0 +1,189 @@ +--- +title: "Build-First: Warum Bauen besser ist als Kaufen" +description: "Software-Miete vs. digitales Eigentum: Warum maßgeschneiderte Systeme am Ende die wirtschaftlichere Wahl sind." +date: "2026-02-05" +tags: ["architecture", "business"] +--- + + + "Gekauft ist schneller als gebaut." – In der digitalen Welt ist das oft + der teuerste Irrtum, den ein Unternehmen begehen kann. + + + In meiner Arbeit als Digital Architect sehe ich täglich, wie + Standard-SaaS-Lösungen Innovationen im Keim ersticken. + + + Sie bezahlen für Features, die Sie nicht brauchen, während Ihnen die + entscheidenden 5 % fehlen. + + + Ich zeige Ihnen, warum Bauen die neue Form der Effizienz{" "} + ist und wie Sie sich echte Marktvorteile sichern. + + +

Die Sackgasse der Generic-Software

+ + Standard-Software ist darauf ausgelegt, dem kleinsten gemeinsamen Nenner + zu gefallen. + + + Man bekommt ein schnelles Resultat, läuft aber sofort gegen eine Wand, + wenn man Prozesse wirklich optimieren will. + + + Ihre Wettbewerber nutzen wahrscheinlich exakt die gleiche Software wie + Sie. + + + Wo bleibt da der technologische Vorsprung? + + + Wer nur mietet, wird niemals Marktführer. Wahre Überlegenheit entsteht + durch maßgeschneiderte Systeme. + + +
+ +graph TD + Need["Individuelles Business-Bedürfnis"] --> Path["Strategische Entscheidung"] + Path --> Buy["Software-Abo (SaaS)"] + Path --> Build["Bespoke Architecture (Mintel)"] + Buy --> Compromise["Kompromisse & Monatliche Fixkosten"] + Build --> Competitive["Wettbewerbsvorteil & Unendliche Freiheit"] + Compromise --> Stagnation["Digitaler Stillstand"] + Competitive --> Growth["Skalierung ohne Grenzen"] + style Build fill:#4ade80,stroke:#333 + style Growth fill:#4ade80,stroke:#333 + +
+ Build vs. Buy: Investieren Sie in Ihr eigenes geistiges Eigentum statt + in die monatliche Miete von Fremdprodukten. +
+
+ +

Bauen bedeutet heute: Strategisches Kombinieren

+ + "Bauen" heißt heute nicht mehr, jedes Rad neu zu erfinden. + + + Ich nutze moderne Frameworks und spezialisierte Microservices, um Ihr + individuelles System zu komponieren. + + + Das Ergebnis ist so flexibel wie eine Eigenentwicklung, aber so schnell + einsatzbereit wie ein Standardprodukt. + + + Dabei besitzen Sie den Code und kontrollieren die Roadmap. + + + Das ist digitale Handwerkskunst am Puls der Zeit. + + +

Der wirtschaftliche Case von 'Build-First'

+ + Die initialen Kosten für Individualsoftware wirken oft höher als eine + monatliche Lizenzgebühr. + + + Doch bei SaaS-Modellen steigen die Kosten linear mit Ihrem Wachstum. + + + Maßgeschneiderte Software amortisiert sich oft nach 12-18 Monaten – durch + wegfallende Lizenzen und massiv gesteigerte Effizienz. + + + Sie investieren in ein Firmen-Asset, das den Wert Ihres + Unternehmens steigert. + + + Software-Miete ist ein Kostenblock, Software-Bau ist eine Investition. + + +
+ +timeline + title Typischer Build-First Projektverlauf + Monat 1-2 : Strategische Planung & Blueprint + Monat 3-4 : Core-Architektur & MVP + Monat 5-6 : Feature-Ausbau & Testing + Monat 7-8 : Launch & Optimierung + Monat 9+ : Kontinuierliche Evolution + +
+ Von der Vision zum skalierbaren System: Ein strukturierter Weg zur digitalen Souveränität. +
+
+ + + + Exakter Prozess-Match: Das System passt sich Ihren + Abläufen an, nicht umgekehrt. Keine unnötigen Klicks mehr. + + + Skalierung nach Ihren Regeln: Keine künstlichen Limits + durch Nutzerzahlen oder Datenvolumen. + + + Sicherheits-Vorsprung: Ihr System ist kein Ziel für + Massen-Exploits, die Standard-Systeme täglich bedrohen.{" "} + Individualität ist Schutz. + + + +
+ +
+ +

Digitales Eigentum als strategischer Hebel

+ + Wer den Code besitzt, besitzt die Zukunft seines Unternehmens. + + + Wenn Sie jemals an einen Exit oder eine Fusion denken, ist technische + Unabhängigkeit ein entscheidender Faktor. + + + Ich schaffe Ihnen eine Architektur, die{" "} + frei von technologischen Altlasten ist. + + + Bauen wir Systeme, die so einzigartig sind wie Ihre Vision. + + +

Für wen ich 'Unikate' erschaffe

+ + Ich bin der Architekt für Gründer, die keine Lust mehr auf "geht technisch + leider nicht" haben. + + + Ist Ihr Business-Modell am Markt einzigartig? Dann sollte es Ihre Software + auch sein. + + + Ich steige dort ein, wo{" "} + technische Brillanz zur strategischen Waffe wird. + + +

Fazit: Hören Sie auf zu mieten, fangen Sie an zu bauen

+ + Wahrer Reichtum im Digitalen entsteht durch Eigentum und Souveränität. + + + Lassen wir gemeinsam ein System erschaffen, das genau so hart arbeitet wie + Sie. + + + + Qualität ist kein Zufallsprodukt, sondern eine bewusste Entscheidung für + den Bau. + {" "} + Ihr Erfolg verdient ein Original. + diff --git a/apps/web/content/blog/builder-systems-threaten-independence.mdx b/apps/web/content/blog/builder-systems-threaten-independence.mdx new file mode 100644 index 0000000..cb24ead --- /dev/null +++ b/apps/web/content/blog/builder-systems-threaten-independence.mdx @@ -0,0 +1,169 @@ +--- +title: "Baukasten-Systeme bedrohen Ihre Unabhängigkeit" +description: "Vendor Lock-In verstehen und vermeiden: Warum eine maßgeschneiderte Architektur Ihr wertvollstes Asset ist." +date: "2026-02-08" +tags: ["architecture", "business"] +--- + + + "Wir können nicht wechseln, das wäre zu teuer." + + + In meiner Arbeit als Digital Architect ist das der Anfang vom Ende jeder + technologischen Innovation. + + + Vendor Lock-In ist die digitale Version einer Geiselnahme + . + + + Ich zeige Ihnen, wie wir Systeme bauen, die Ihnen jederzeit die volle + Freiheit lassen – technologisch und wirtschaftlich. + + +

Die unsichtbaren Ketten proprietärer Systeme

+ + Viele Unternehmen lassen sich von der Bequemlichkeit großer + SaaS-Plattformen oder Baukästen blenden. + + + Man bekommt ein schnelles Feature, gibt aber dafür die Kontrolle über + seine Daten und seine Codebasis ab. + + + Nach zwei Jahren sind Sie so tief im Ökosystem eines Anbieters verstrickt, + dass ein Auszug unmöglich scheint. + + + Der Anbieter weiß das – und diktiert fortan die Preise und das Tempo Ihrer + Entwicklung. + + + Ich nenne das technologische Erpressbarkeit. + + + Wahre Unabhängigkeit beginnt bei der strategischen Wahl der Architektur. + + +
+ +graph TD + Prop["Proprietäre Blackbox"] --> Lock["Steigende Kosten & Starre Features"] + Open["Open-Source-Kern & Offene Standards"] --> Flex["Volle Kontrolle & Anbieter-Freiheit"] + Lock --> Crisis["Digitale Sackgasse"] + Flex --> Evolution["Permanente Innovation"] + style Open fill:#4ade80,stroke:#333 + style Crisis fill:#fca5a5,stroke:#333 + +
+ Die Gabelung der digitalen Strategie: Wählen Sie Freiheit durch + Architektur, statt Komfort durch Abhängigkeit. +
+
+ +

Technologische Souveränität als Asset

+Software sollte für Sie arbeiten, nicht umgekehrt. + + Indem wir auf offene Standards und portable Architekturen setzen, + verwandeln wir Code in ein echtes Firmen-Asset. + + + Sie können den Cloud-Anbieter wechseln, die Agentur tauschen oder das Team + skalieren – ohne jemals bei Null anfangen zu müssen. + +Das ist das Privileg der technologischen Elite. + + Portabilität ist kein technisches Gimmick, sondern eine unternehmerische + Notwendigkeit. + + +

Meine Architektur der Ungebundenheit

+Ich baue keine "Käfige" aus fertigen Plugins. +Mein Framework basiert auf Modularität und Klarheit. + + + + Standard-basiertes Engineering: Wir nutzen + Technologien, die weltweit verstanden werden. Keine geheimen + "Spezial-Module" eines einzelnen Anbieters. + + + Daten-Portabilität: Ihre Daten gehören Ihnen. Zu jeder + Zeit. Wir bauen Schnittstellen, die den Export so einfach machen wie den + Import. + + + Cloud-agnostisches Hosting: Wir nutzen + Container-Technologie. Ob AWS, Azure oder lokale Anbieter –{" "} + Ihr Code läuft überall gleich perfekt. + + + +
+ +stateDiagram-v2 + ["*"] --> Independent + Independent --> Integrated : Adopt Platform + Integrated --> Dependent : Deep Integration + Dependent --> Locked : Critical Mass + Locked --> Migration : Exit Decision + Migration --> Independent : Successful Exit + Integrated --> Independent : Early Exit + Locked --> ["*"] + +
+ Der Weg in die Abhängigkeit: Je tiefer die Integration, desto schwieriger der Ausstieg. +
+
+ +
+ +
+ +

Der strategische Hebel für langfristige Rendite

+Systeme ohne Lock-In altern besser. + + Sie lassen sich schrittweise modernisieren, statt alle fünf Jahre komplett + neu gebaut werden zu müssen. + + + Das spart Millionen an Opportunitätskosten und Fehl-Investitionen. + +Seien Sie der Herr über Ihr digitales Schicksal. + + Investieren Sie in intelligente Unabhängigkeit. + + +

Für wen ich 'Freiheits-Systeme' erstelle

+ + Ich arbeite für Gründer, die ihr Unternehmen langfristig wertvoll + aufstellen wollen. + + + Ist digitale Exzellenz Teil Ihrer Exit-Strategie oder Ihres Erbes? Dann + brauchen Sie meine Architektur. + + + Ich baue keine Provisorien, sondern nachhaltige Werte. + + +

Fazit: Freiheit ist eine Wahl

+ + Technologie sollte Ihnen Flügel verleihen, keine Fesseln anlegen. + + + Lassen wir gemeinsam ein System schaffen, das so flexibel ist wie Ihr + Business. + + + + Werden Sie unersetzbar durch Qualität, nicht durch Abhängigkeit. + + Ihr Erfolg verdient absolute Freiheit. + diff --git a/apps/web/content/blog/clean-code-for-business-value.mdx b/apps/web/content/blog/clean-code-for-business-value.mdx new file mode 100644 index 0000000..eb6e218 --- /dev/null +++ b/apps/web/content/blog/clean-code-for-business-value.mdx @@ -0,0 +1,154 @@ +--- +title: "Clean Code: Warum technische Qualität Ihr Business skaliert" +description: "Software-Engineering als Wertanlage: Wie saubere Code-Strukturen Ihre Wartungskosten senken und Innovationen beschleunigen." +date: "2026-01-30" +tags: ["development", "business"] +--- + + + Code ist nicht nur eine Anweisung für Maschinen. Es ist das Fundament + Ihres digitalen Unternehmenswertes. + + + In meiner Arbeit als Digital Architect sehe ich oft "historisch + gewachsenen" Code, der eher einem verfilzten Knäuel gleicht als einer + Architektur. + + + Ich zeige Ihnen, warum Clean Code kein Luxus ist, sondern + die harte Währung für Ihre Zukunftsfähigkeit. + + +

Die versteckten Kosten von 'Quick-and-Dirty'

+ + Softwareentwicklung unter Zeitdruck führt oft zu unsauberen Abkürzungen. + + + Kurzfristig spart das Stunden, langfristig erstickt es jede Innovation. + + + Unsauberer Code wird mit jedem Monat schwerer zu warten und zu erweitern. + + + Ich nenne das technologische Verstopfung. + + + Ihre Entwickler verbringen dann 80 % ihrer Zeit mit Bugfixing, statt neue + Features zu bauen. + + + Clean Code hingegen ist eine Investition in die Geschwindigkeit von + morgen. + + +
+ +graph TD + Clean["Clean Code Architektur"] --> Easy["Einfache Wartbarkeit"] + Clean --> Scalable["Schnelle Erweiterbarkeit"] + Easy --> LowCost["Geringe langfristige Kosten"] + Scalable --> Market["Schnellerer Markteintritt (Time-to-Market)"] + LowCost --> Profit["Höherer ROI für Ihr Business"] + Market --> Profit + style Profit fill:#4ade80,stroke:#333 + style Clean fill:#4ade80,stroke:#333 + +
+ Die Logik der Qualität: Sauberer Code zahlt sich durch sinkende + Betriebskosten und steigendes Innovationstempo aus. +
+
+ +

Code als Kommunikationsmittel

+Code wird viel öfter gelesen als geschrieben. + + Deshalb ist Klarheit mein oberstes Gebot. Ein gut strukturiertes System + "erklärt" sich selbst. + + + Das macht Sie unabhängig von einzelnen Personen. Jedes neue Teammitglied + findet sich sofort zurecht. + + + Das ist Souveränität durch Transparenz. + + + Ich schreibe Code für Menschen, nicht nur für den Compiler. + + +

Meine Prinzipien für eine glasklare Architektur

+ + Wie unterscheidet sich meine Arbeit von Standard-Agentur-Code? + + + Durch die kompromisslose Anwendung von Engineering-Prinzipien: + + + + + Single Responsibility: Jede Komponente tut genau eine + Sache – und die perfekt. Das macht Fehlerbehebungen zum Kinderspiel. + + + Automatisierte Selbstkontrolle: Bevor eine Änderung + live geht, wird sie von hunderten automatischen Tests geprüft.{" "} + Qualität ist bei mir systemimmanent. + + + Dokumentation im Design: Ich baue Systeme, deren + Struktur so logisch ist, dass Handbücher überflüssig werden. + + + +
+ +
+ +

Wahrer Profit durch technische Brillanz

+Clean Code senkt Ihre Betriebskosten massiv. + + Es ist die Basis für Skalierbarkeit. Nur ein sauberes Fundament trägt ein + Hochhaus. + + + Wenn Sie planen, Ihr digitales Business über Jahre zu führen, ist{" "} + Codequalität Ihre wichtigste Versicherung. + + + Software sollte ein Vermögenswert sein, keine Verbindlichkeit. + +Gießen wir ein Fundament, das Stand hält. + +

Für wen ich 'Pures Gold' schreibe

+ + Ich bin der Architekt für Entscheider, die den Wert ihrer digitalen Assets + langfristig maximieren wollen. + + + Haben Sie genug von Systemen, die bei jeder Änderung zusammenbrechen? Dann + passen wir zusammen. + + + Ich schaffe Ruhe im Maschinenraum. + + +

Fazit: Qualität gewinnt immer

+Es gibt keine Abkürzung zu exzellenter Software. + + Lassen wir gemeinsam den Ballast von unsauberem Code hinter uns. + + + Ich baue Ihnen eine Architektur, die nicht nur heute funktioniert, sondern + auch in vielen Jahren noch durch ihre{" "} + Eleganz und Klarheit besticht. + + + Purer Code. Purere Ergebnisse. Ihr Erfolg verdient dieses + Niveau. + diff --git a/apps/web/content/blog/crm-synchronization-headless.mdx b/apps/web/content/blog/crm-synchronization-headless.mdx new file mode 100644 index 0000000..25ff8f7 --- /dev/null +++ b/apps/web/content/blog/crm-synchronization-headless.mdx @@ -0,0 +1,159 @@ +--- +title: "CRM-Synchronisation: Daten-Silos architektonisch auflösen" +description: "Vom Kontaktformular direkt in den Sales-Funnel: Wie automatisierte Datenflüsse menschliche Fehler eliminieren und Leads beschleunigen." +date: "2026-01-31" +tags: ["crm", "architecture"] +--- + + + Die wertvollsten Daten Ihres Unternehmens liegen oft in Silos versteckt. + + + Ihre Website sammelt Leads, aber Ihr CRM "weiß" nichts davon – oder erst + nach manueller Übertragung. + +Ich beende das Zeitalter der Daten-Inseln. + + Ich zeige Ihnen, wie nahtlose CRM-Integration Ihre + Marketing-Effizienz verdoppelt und menschliche Fehler eliminiert. + + +

Das Problem der manuellen Daten-Brücke

+ + Viele Unternehmen nutzen Kontaktformulare, die lediglich E-Mails + versenden. + + + Ein Mitarbeiter muss diese E-Mails lesen und die Daten händisch in + Salesforce, HubSpot oder Pipedrive übertragen. + + + Das ist nicht nur zeitfressend, sondern auch eine Riskante Fehlerquelle. + + + Leads gehen verloren, die Reaktionszeit sinkt und die Datenqualität + leidet. + + + In einer digitalen Welt ist{" "} + manuelle Datenpflege ein Anachronismus. + + + Wahre Professionalität bedeutet: Die Software erledigt die Arbeit im + Hintergrund. + + +
+
+ +sequenceDiagram + participant Besucher + participant Website + participant ValidationLayer + participant CRM + participant SalesTeam + Besucher->>Website: Formular absenden + +
+ Der automatisierte Lead-Fluss: Von der ersten Interaktion bis zum CRM-Eintrag in Millisekunden – ohne menschliches Eingreifen. +
+
+
+ +

Echtzeit-Synchronität als Wettbewerbsvorteil

+Kunden erwarten heute sofortige Reaktionen. + + Ein Lead, der erst nach 24 Stunden kontaktiert wird, ist oft schon beim + Wettbewerber gelandet. + + + Meine Architektur sorgt dafür, dass Ihre Sales-Teams{" "} + Sekunden nach dem Klick arbeitsfähig sind. + + + Wir automatisieren die Qualifizierung und Zuweisung, damit keine Zeit + verloren geht. + + + Geschwindigkeit ist im Digitalvertrieb der entscheidende Faktor. + + +

Meine Hebel for Ihre Daten-Souveränität

+ + Integration bedeutet für mich mehr als nur das Verbinden von APIs. + + + Es geht um die Schaffung eines Single Source of Truth. + + + + + Resiliente API-Anbindung: Wir bauen Puffer-Systeme. + Sollte Ihr CRM kurzzeitig offline sein, gehen keine Leads verloren. + + + Smart Data Enrichment: Wir bereiten die Daten so auf, + dass Ihr Sales-Team sofort alle Kontext-Infos hat. + + + DSGVO-konforme Übertragung: Alle Daten fließen + Ende-zu-Ende verschlüsselt und nach strengsten europäischen Standards.{" "} + Sicherheit trifft auf Komfort. + + + +
+ +
+ +

Warum die 'Billig-Lösung' hier teuer wird

+ + Einfache Plugins für CRM-Anbindungen sind oft instabil und unflexibel. + + + Sie brechen bei Updates oder können individuelle Felder nicht korrekt + abbilden. + + + Ich baue Ihnen eine robuste Brücke, die mit Ihren + Anforderungen mitwächst. + + + Verlässlichkeit ist in der Lead-Generierung die wichtigste Eigenschaft. + + + Vermeiden Sie das Risiko von Daten-Silos durch professionelles + Engineering. + + +

Für wen ich 'Daten-Autobahnen' baue

+ + Mein Fokus liegt auf Unternehmen, deren Erfolg von der Qualität ihrer + Leads abhängt. + + + Begreifen Sie Ihr CRM als Herzstück Ihrer Wertschöpfung? + Dann bin ich Ihr Architekt. + + + Ich schlage die Brücke zwischen Ihrer Website und Ihrem Erfolg. + + +

Fazit: Lassen Sie Ihre Daten fließen

+ + Technologie sollte Reibung eliminieren, nicht neue Hürden schaffen. + + + Lassen wir gemeinsam Ihre Website zu einem integralen Bestandteil Ihres + Sales-Motors machen. + + + Präzision in der Schnittstelle, Klarheit im Ergebnis. Ihr + Business verdient einen reibungslosen Datenfluss. + diff --git a/apps/web/content/blog/digital-longevity-architecture.mdx b/apps/web/content/blog/digital-longevity-architecture.mdx new file mode 100644 index 0000000..1683b6b --- /dev/null +++ b/apps/web/content/blog/digital-longevity-architecture.mdx @@ -0,0 +1,152 @@ +--- +title: "Digital Longevity: Architektur für das nächste Jahrzehnt" +description: "Software ohne Verfallsdatum: So bauen wir Systeme, die technologische Trends überdauern und langfristige Werte schaffen." +date: "2026-02-02" +tags: ["architecture", "longevity"] +--- + + + In der schnelllebigen Tech-Welt gilt Software oft schon nach zwei Jahren + als veraltet. + + + Ich halte das für eine massive Verschwendung von Kapital und Energie. + + + Wahre Qualität zeigt sich darin, wie ein System altert. + + + Ich zeige Ihnen, wie wir digitale Werte für Jahrzehnte{" "} + schaffen – durch vorausschauende Architektur und zeitlose Standards. + + +

Gegen die Wegwerf-Mentalität im Code

+ + Viele Agenturen bauen "Schönwetter-Lösungen", die nur bis zur nächsten + Trend-Welle halten. + + + Man setzt auf kurzlebige Frameworks oder proprietäre Blackboxes, die nach + kurzer Zeit nicht mehr unterstützt werden. + + + Das Ergebnis: Alle drei Jahre ist ein teurer Relaunch fällig. + + + Ich nenne das geplante Obsoleszenz der Software. + + + Mein Boutique-Ansatz ist das Gegenteil davon: Ich baue Systeme, die durch + ihre innere Ordnung und Robustheit bestechen. + + + Guter Code ist wie eine solide Immobilie – er braucht Pflege, aber keine + Abrissbirne. + + +
+ +graph TD + Logic["Zukunftsfähige Kern-Logik"] --> Standards["Offene Web-Standards"] + Logic --> Modular["Modulare Komponenten"] + Standards --> Decade["Lebensdauer > 10 Jahre"] + Modular --> Update["Einfache Teil-Modernisierung"] + Decade --> ROI["Maximaler Return on Investment"] + Update --> ROI + style ROI fill:#4ade80,stroke:#333 + style Decade fill:#4ade80,stroke:#333 + +
+ Architektur der Langlebigkeit: Durch die Trennung von Logik und Trends + sichern wir den Wert Ihrer digitalen Investition über Generationen. +
+
+ +

Die Ästhetik der Zeitlosigkeit

+Langlebigkeit hat auch eine visuelle Komponente. + + Ich vermeide "modische" Spielereien, die morgen schon peinlich wirken + könnten. + + + Wahre technische Eleganz ist schlicht, funktional und hochpräzise. + + + Ein industrieller, klarer Look altert langsamer als jede + verspielte Grafik. + + + Ich schaffe Designs, die heute beeindrucken und in fünf Jahren noch immer + souverän wirken. + + +

Meine Prinzipien für ewige Systeme

+Wie baut man Software, die nicht veraltet? +Durch die kompromisslose Auswahl der Fundamente: + + + + Bohrmaschinen statt Spielzeug: Ich nutze nur + Technologien mit breitem industriellem Rückhalt. Keine "Hype"-Tools ohne + Langzeit-Sicherheit. + + + Strict Separation of Concerns: Wir trennen Design, + Daten und Logik so sauber, dass man Einzelteile austauschen kann, ohne + das Ganze zu gefährden. + + + Automatisierte Evolution: Mein System prüft sich + selbst. Wir erkennen frühzeitig, wenn externe Standards sich ändern und + passen uns proaktiv an. + + + +
+ +
+ +

Rendite durch technologische Beständigkeit

+Wahrer ROI entsteht erst über die Zeit. + + Wer nicht ständig neu bauen muss, hat mehr Kapital für echtes Wachstum zur + Verfügung. + + + Langlebige Software ist zudem das{" "} + nachhaltigste digitale Werkzeug, das Sie besitzen können. + + + Investieren Sie in Substanz, nicht in flüchtige Effekte. + +Vorsprung hat der, dessen Fundament felsenfest steht. + +

Wann ist 'Ewigkeit' Ihr Ziel?

+ + Suchen Sie einen Partner für den Aufbau einer digitalen Marke, die Bestand + hat? + + + Ich arbeite für Gründer, die Generationen-Projekte{" "} + führen. + + + Wenn Sie technologische Stabilität als Teil Ihres Vermächtnisses + begreifen, bin ich Ihr Architekt. + + +

Fazit: Werte schaffen, die bleiben

+Digitale Exzellenz misst sich am Erfolg von morgen. + + Lassen wir gemeinsam ein System gießen, das die Zeit überdauert. + + + Qualität ist Beständigkeit. Ihr Erfolg verdient eine + Architektur ohne Verfallsdatum. + diff --git a/apps/web/content/blog/fixed-price-digital-projects.mdx b/apps/web/content/blog/fixed-price-digital-projects.mdx new file mode 100644 index 0000000..fb1723c --- /dev/null +++ b/apps/web/content/blog/fixed-price-digital-projects.mdx @@ -0,0 +1,205 @@ +--- +title: "Der strategische Festpreis: Warum Budgetsicherheit Qualität fördert" +description: "Keine Angst vor unendlichen Stunden: Erfahren Sie, warum ein Festpreis-Modell der fairste Weg zu exzellenten digitalen Produkten ist." +date: "2026-02-04" +tags: ["management", "business"] +--- + + + Sicherheitsdenken ist tief in der menschlichen Natur verwurzelt. + + + Am Ende eines Projekts wollen wir wissen, worauf wir uns eingelassen haben + – finanziell und zeitlich. + + + In meiner Arbeit als Digital Architect baue ich{" "} + Sicherheit durch Transparenz. + + + Ich zeige Ihnen, warum ein intelligenter Festpreis der fairste Weg zu + exzellenten Ergebnissen ist. + + +

Die Falle der unendlichen Stunden

+ + In der klassischen Softwareentwicklung ist Abrechnung nach Stunden (Time & + Material) der Standard. + + + Doch das setzt einen völlig falschen Anreiz: Je länger ein Entwickler + braucht, desto mehr verdient er. + +Effizienz wird ökonomisch bestraft. + + Ich hingegen habe mein gesamtes Business auf Geschwindigkeit und Klarheit + optimiert. + + + Mit einem Festpreis drehen wir den Spieß um:{" "} + Mein Anreiz ist Ihre schnellstmögliche Zufriedenheit. + + + So entsteht echte Partnerschaft statt eines Interessenkonflikts. + + +
+ +graph LR + Goal["Ihr Projektziel"] --> Model["Wirtschaftliches Modell"] + Model --> TnM["Abrechnung nach Stunden"] + Model --> Fixed["Strategischer Festpreis (Mintel)"] + TnM --> Risk["Uferlose Kosten & Zeitdruck"] + Fixed --> Safety["Kalkulations-Sicherheit & Fokus"] + Safety --> Quality["Maximale Qualität durch Effizienz"] + style Fixed fill:#4ade80,stroke:#333 + style Quality fill:#4ade80,stroke:#333 + +
+ Das Modell des Vertrauens: Fixe Budgets schaffen den Raum für + kompromisslose inhaltliche Qualität. +
+
+ +

Planung ist das halbe Fundament

+ + Ein Festpreis funktioniert nur, wenn die Vision klar ist. + + + Deshalb investiere ich zu Beginn massiv Zeit in die Analyse und das + "Blueprint-Design". + + + Wenn wir das Fundament präzise geplant haben, gibt es im Bauprozess keine + bösen Überraschungen. + + + Das ist digitales Engineering mit norddeutscher Klarheit. + + + Sie bezahlen nicht für mein Ausprobieren, sondern für die punktgenaue + Umsetzung meiner Erfahrung. + + +

Warum meine Kalkulation hält

+Ich arbeite nicht mit Schätzungen, sondern mit Daten. + + Durch meine automatisierte Toolchain weiß ich exakt, wie lange bestimmte + Architekturschritte dauern. + + + + + Integrierte Risiko-Abdeckung: Unvorhergesehenes ist in + meinem Preis bereits einkalkuliert. Sie tragen kein finanzielles Risiko. + + + Klare Deliverables: Wir definieren präzise + Meilensteine. Sie wissen zu jedem Zeitpunkt, was Sie für Ihr Investment + erhalten. + + + 0 % Budget-Overrun: Mein Wort gilt.{" "} + + Zusatzkosten entstehen nur, wenn Sie den Scope aktiv erweitern. + + + + + + +
+ +
+ +

Ihr Investment in Sicherheit

+Ein Festpreis befreit den Kopf. + + Statt bei jedem Meeting die Uhr hämmern zu hören, konzentrieren wir uns + auf das Wesentliche: Ihren Markterfolg. + + + Das schafft eine Atmosphäre von{" "} + Kreativität und technologischem Mut. + + + Wer keine Angst vor dem Budget hat, baut die besseren Lösungen. + +Ich schaffe Ihnen den Rahmen für diese Exzellenz. + +

Wann ist ein Festpreis der richtige Weg?

+ + Mein Modell ist ideal für Entscheider, die unternehmerische Verantwortung + für Budgets tragen. + + + Ist Ihnen ein{" "} + planbares Ergebnis wichtiger als vage Hoffnungen? Dann + passen wir zusammen. + + + Ich arbeite für die Profis, die Qualität zum festen Preis wertschätzen. + + +

Fazit: Klarheit ist kein Luxus

+ + Lassen wir das Rätselraten bei der Preisgestaltung hinter uns. + + + Ich biete Ihnen eine Zusammenarbeit auf Augenhöhe, bei der das Ergebnis im + Mittelpunkt steht. + + + Lassen Sie uns gemeinsam Ihr Projekt auf ein{" "} + solides finanzielles Fundament stellen. + + + Pünktlich. Präzise. Zum vereinbarten Preis. + diff --git a/apps/web/content/blog/gdpr-conformity-system-approach.mdx b/apps/web/content/blog/gdpr-conformity-system-approach.mdx new file mode 100644 index 0000000..8a272fb --- /dev/null +++ b/apps/web/content/blog/gdpr-conformity-system-approach.mdx @@ -0,0 +1,173 @@ +--- +title: "DSGVO-Konformität: Ein systemischer Architektur-Ansatz" +description: "Warum Datenschutz kein Banner-Problem ist: So bauen Sie rechtssichere Systeme durch kluges Engineering." +date: "2026-02-10" +tags: ["legal", "gdpr"] +--- + + + DSGVO-Konformität wird oft als lästiges bürokratisches Hindernis + wahrgenommen. + + + In meiner Arbeit als Digital Architect sehe ich sie jedoch als + das ultimative Qualitätsmerkmal für sauberes Engineering. + + + Ein System, das Daten schützt, ist ein gesundes System. + + + Ich zeige Ihnen, wie ich Datenschutz architektonisch löse, statt ihn nur + mit Bannern zu "flicken". + + +

Gegen das Abmahnrisiko – mit Systemarchitektur

+ + Die meisten versuchen, die Anforderungen durch rechtliche Dokumente und + nachträglich installierte Banner zu lösen. + + + Das ist so, als würde man ein brennendes Haus mit einer neuen + Versicherungspolice löschen wollen. + + + Es schafft eine Schein-Sicherheit, bekämpft aber nicht die Ursache des + Risikos. + + + Datenschutz muss bereits in der DNA des Codes verankert sein (Privacy by + Design). + + + Wenn ein System keine unnötigen Daten sammelt,{" "} + verschwinden die Einfallstore für Probleme von selbst. + +Wahre Compliance ist technisch erzwungen. + +
+ +graph TD + Collection["Frontend Datenerhebung"] --> Minimize["Strikte Datenminimierung (SSO)"] + Minimize --> Encrypt["End-to-End Verschlüsselung (TLS 1.3)"] + Encrypt --> Access["Rollenbasierte Zugriffskontrolle (RBAC)"] + Access --> Audit["Automatisches Compliance-Logging"] + Audit --> Safe["Rechtssicheres & Robustes System"] + style Safe fill:#4ade80,stroke:#333 + style Minimize fill:#4ade80,stroke:#333 + style Encrypt fill:#4ade80,stroke:#333 + +
+ Der Kreislauf der systemischen Sicherheit: Jede Stufe schützt Ihre + Daten, Ihren Ruf und Ihren langfristigen Business-Value. +
+
+ +

Die ökonomische Logik des Datenschutz-Management

+ + Wussten Sie, dass unsaubere Datensparsamkeit ein echtes finanzielles + Risiko darstellt? + + + Daten, die Sie nicht besitzen, können nicht gestohlen werden. + + + Ich helfe Ihnen, Ihre Prozesse so zu verschlanken, dass nur das{" "} + geschäftskritische Minimum fließt. + + + Dies spart Speicher- und Rechenkapazitäten und reduziert Haftungsrisiken + massiv. + +Ein schlankes Datensystem ist ein agiles System. + + Compliance ist kein Kostenfaktor, sondern eine Versicherung für Ihre + digitale Zukunft. + + +

Mein Prinzip: Privacy by Infrastructure

+ + Ich betrachte Datenschutz nicht als Text auf der Unterseite "Impressum", + sondern als Eigenschaft der Infrastruktur. + + + Mein Ziel ist ein System, bei dem die Einhaltung der Regeln{" "} + technisch unvermeidbar ist. + + + + + Echtzeit-Anonymisierung: Logfiles und IPs werden am + Punkt des Eingangs anonymisiert. Was technisch unkenntlich ist, fällt + nicht unter die DSGVO-Strenge. + + + Geschlossene Datenkreisläufe: Wir vermeiden + Blackbox-Server von Drittanbietern. Jedes Formular nutzt dedizierte + Kanäle. + + + Compliance-as-Code: Automatisierte Tests prüfen bei + jeder Änderung, ob neue Abhängigkeiten Ihre Richtlinien gefährden. + + + +
+ +
+ +

Vertrauen als härteste Währung am Markt

+ + Nutzer und Kunden sind heute sensibilisierter als jemals zuvor. + + + Eine Website, die respektvoll und ohne Banner-Nötigung mit Daten umgeht, + schafft sofortiges Vertrauen. + + + In einer Welt voller "Dark Patterns" ist Transparenz Ihr stärkstes + Verkaufsargument. + + + Ich schaffe souveräne digitale Räume, die für Ihre Marke + und Ihre Werte sprechen. + + + Ein sauberes DSGVO-Konzept ist ein Investment in die Reputation Ihres + Unternehmens. + + +

Für wen ich 'Sicherheits-Festungen' baue

+ + Mein architektonischer Ansatz ist ideal für Unternehmen in regulierten + Branchen. + + + Begreifen Sie{" "} + technische Exzellenz als Teil Ihrer Verantwortung? Dann + sind wir Partner. + + + Ich werde aktiv, wenn Sicherheit für Sie nicht verhandelbar ist. + + +

Fazit: Souveränität durch saubere Technik

+ + Schaffen wir die Angst vor rechtlichen Fehltritten ab. + + + Ich baue Ihnen ein System, das durch seine innere Ordnung besticht und den + Schutz technically garantiert. + + + Lassen wir Ihre digitale Basis für die + nächste Stufe der Professionalität gemeinsam gießen. + + + Seriosität ist planbar. + diff --git a/apps/web/content/blog/green-it-sustainable-web.mdx b/apps/web/content/blog/green-it-sustainable-web.mdx new file mode 100644 index 0000000..cfe2af7 --- /dev/null +++ b/apps/web/content/blog/green-it-sustainable-web.mdx @@ -0,0 +1,169 @@ +--- +title: "Green IT: Warum nachhaltiger Code profitabler ist" +description: "Effizienz als ökologischer und ökonomischer Hebel: Wie schlanke Web-Architekturen CO2 sparen und Ihr Budget schonen." +date: "2026-02-03" +tags: ["sustainability", "performance"] +--- + + + Das Internet verbraucht mehr Energie als der weltweite Flugverkehr. + + + In meiner Rolle als Digital Architect sehe ich Nachhaltigkeit nicht als + "Nice-to-Have", sondern als Ausdruck technischer Reife. + + + Effizienter Code ist grüner Code.{" "} + + Schlanke Systeme sparen nicht nur CO2, sondern auch bares Geld. + + + + Ich zeige Ihnen, warum ökologische Verantwortung und ökonomische + Profitabilität Hand in Hand gehen. + + +

Der ökologische Fußabdruck von schlechtem Code

+ + Jedes unnötige Kilobyte, das durch das Netz geschickt wird, frisst Strom – + im Rechenzentrum, in den Leitungen und am Endgerät des Nutzers. + + + Viele moderne Websites sind heute "Adipös". Sie schleppen Megabytes an + Ballast mit sich herum. + + + Das führt zu erhitzten Smartphones und überlasteten Servern. + + + Ich nenne das digitale Verschwendung. + + + Durch radikale Optimierung senken wir die CPU-Last um bis zu 80 %. Das + schont die Umwelt und beschleunigt Ihre UX. + + +
+ +graph TD + Code["Schlanke Code-Architektur"] --> Compute["Weniger CPU-Zyklen am Server"] + Code --> Bytes["Weniger Traffic (CDN)"] + Compute --> Energy["Niedrigerer Stromverbrauch"] + Bytes --> Impact["Schnellere UX & Weniger CO2"] + Energy --> Profit["Geringere Hosting-Kosten"] + style Profit fill:#4ade80,stroke:#333 + style Impact fill:#4ade80,stroke:#333 + +
+ Die grüne Rendite: Effizienz in der Software führt direkt zu + ökologischen und finanziellen Einsparungen. +
+
+ +

Boutique-Engineering als Klimaschutz

+ + Standard-Agenturen greifen oft zu überladenen Baukästen, um Zeit zu + sparen. + + + Der Preis dafür ist eine gigantische technische Ineffizienz. + +Ich investiere lieber Zeit in präzises Handwerk. + + Ein maßgeschneidertes System verbraucht nur einen Bruchteil der Ressourcen + eines WordPress-Monolithen. + + + Das ist Nachhaltigkeit durch technologische Brillanz. + + +

Meine Hebel für eine nachhaltige Infrastruktur

+Grüne IT beginnt bei der Wahl der Waffen. + + Hier sind drei Wege, wie ich Ihren digitalen Fußabdruck minimiere: + + + + + Static-First Architektur: Wir berechnen Seiten nicht + bei jedem Aufruf neu. Einmal generiert, tausende Male effizient + ausgeliefert. + + + Intelligentes Asset-Management: Keine unnötigen Fonts + oder Tracking-Skripte. Wir senden nur das absolute Minimum an Daten an + den Browser. + + + Grünes Server-Partnering: Ich wähle Hostinganbieter, + die zu 100 % mit erneuerbaren Energien arbeiten.{" "} + Nachhaltigkeit über den gesamten Stack. + + + +
+ +pie + "Server Computing" : 40 + "Data Transfer" : 30 + "Client Rendering" : 20 + "Asset Storage" : 10 + +
+ Wo Ihre Website Energie verbraucht: Optimierungspotenzial in jedem Bereich. +
+
+ +
+ +
+ +

Wirtschaftliche Vorteile von Green IT

+Effizienz zahlt sich aus. + + Weniger Datentransfer und geringere Serverlast bedeuten niedrigere + laufende Kosten. + + + Gleichzeitig belohnt Google schnelle, schlanke Seiten mit besseren + Rankings. + + + Nachhaltigkeit ist also kein Verzicht, sondern ein{" "} + Wettbewerbsvorteil. + + + Positionieren Sie Ihr Unternehmen als Vorreiter einer neuen, bewussten + digitalen Ära. + + +

Wann macht Green IT für Sie Sinn?

+ + Ich baue für Marken, die Werte über kurzfristige Trends{" "} + stellen. + + + Wenn Sie Ihre CSR-Ziele auch digital ernst nehmen, bin ich Ihr Architekt. + + + Schaffen wir Systeme, die auch für die nächste Generation noch vorbildlich + sind. + + +

Fazit: Weniger ist mehr Zukunft

+Gutes Design ist immer auch sparsames Design. + + Lassen wir gemeinsam Ihren digitalen Ballast abwerfen und stattdessen in + echte Effizienz investieren. + + + Purer Output bei minimalem Input. Das ist das Ziel meines + Boutique-Ansatzes. + +Für Ihr Business und unseren Planeten. diff --git a/apps/web/content/blog/hidden-costs-of-wordpress-plugins.mdx b/apps/web/content/blog/hidden-costs-of-wordpress-plugins.mdx new file mode 100644 index 0000000..64ac7b4 --- /dev/null +++ b/apps/web/content/blog/hidden-costs-of-wordpress-plugins.mdx @@ -0,0 +1,159 @@ +--- +title: "Die versteckten Kosten von WordPress-Plugins" +description: "Wie Sie die Plugin-Falle vermeiden und eine wartbare, sichere Plattform aufbauen." +date: "2026-02-12" +tags: ["wordpress", "performance"] +--- + + + WordPress-Plugins werden oft als die ultimative Abkürzung zum Erfolg + verkauft. + + + In meiner täglichen Praxis als Digital Architect sehe ich jedoch meist das + Gegenteil: Sie sind eine teure Umleitung in eine technische Sackgasse. + + + Die vermeintlich "schnelle Lösung" ist am Ende{" "} + oft die teuerste Entscheidung Ihrer digitalen Strategie. + + +

Die "Frankenstein-Architektur" der Plugins

+ + Die Versuchung ist menschlich: Ein Klick, und das neue Feature ist da. + + + Doch was Sie wirklich tun, ist fremden Code ungefiltert in Ihr + geschäftskritisches System zu lassen. + + + Ich sehe oft Instanzen, die unter der Last von 40+ Plugins förmlich + zermalmt werden. + + + Jedes Plugin verfolgt eine eigene Logik und kämpft mit anderen Komponenten + um knappe Ressourcen. + + + Es entsteht eine instabile "Frankenstein-Architektur". + + + Sie geben die Kontrolle über Ihre Plattform ab und hängen von der Roadmap + Dritter ab. + + + Verliert ein Plugin-Entwickler das Interesse, stehen Sie mit einer + Sicherheitslücke da. + + +
+ +graph TD + P1["Plugin A (Slider)"] --> Core["WordPress Core"] + P2["Plugin B (SEO)"] --> Core + P3["Plugin C (Forms)"] --> Core + Core --> Bloat["Asset-Overload (CSS/JS)"] + Bloat --> Slow["Ladezeit > 4 Sek."] + P1 -.-> P2["Konfliktmöglichkeit"] + P2 -.-> P3["Konfliktmöglichkeit"] + Slow --> Bounce["Besucher springen ab"] + style Slow fill:#fca5a5,stroke:#333 + style Bounce fill:#ef4444,color:#fff + +
+ Das Plugin-Paradoxon: Jedes 'Feature' erhöht die Wahrscheinlichkeit + eines Systemkollapses. +
+
+ +

Die versteckten Kosten der "Gratis"-Features

+ + Man sagt oft, WordPress-Plugins seien kostenlos. Das ist eine gefährliche + Illusion. + +Die wahren Kosten entstehen bei der Wartung. + + In klassischen Agenturen zahlt ein Kunde meist hunderte Euro monatlich für + "Updates installieren". + + + Das ist reaktives Hoffen statt proaktivem Engineering. + + + Echter, sauberer Code altert viel langsamer als zusammengeklickte + Plugin-Konstrukte. + + +

Technische und betriebswirtschaftliche Risiken

+ + Aus meiner architektonischen Sicht gibt es drei kritische Hebel: + + + + Die Sicherheitsfalle: Über 90 % der Angriffe auf + WordPress erfolgen über unsichere Plugins. Jede Erweiterung vergrößert + Ihre Angriffsfläche. + + + Die Performance-Erosion: Viele Plugins laden ihre + Scripte global – auch wenn sie gar nicht gebraucht werden. + + + Die technologische Sackgasse: Je mehr Plugins Sie + nutzen, desto schwerer wird ein Wechsel.{" "} + Datenhoheit beginnt bei der Codehoheit. + + + +
+ +
+ +

Mein Weg: Präziser Code statt Blackbox-Plugins

+ + Anstatt ein tonnenschweres Plugin für eine einfache Funktion zu + installieren, schreibe ich Ihnen diese Funktion direkt. + + + Das Ergebnis ist ein System, das exakt das tut, was Sie brauchen – und + kein Byte mehr. + + + Kein Ballast, kein Sicherheitsrisiko, keine Abhängigkeit. + + + Ich baue keine digitalen Kartenhäuser, sondern{" "} + echte digitale Assets, die für Profis arbeiten. + + +

Wann ist dieser Premium-Ansatz für Sie richtig?

+ + Ich arbeite für die Wenigen, die{" "} + technische Brillanz zum entscheidenden Marktvorteil + machen wollen. + + + Planen Sie, Ihre Marke über Jahre stabil im Netz zu führen? Dann sind wir + Partner. + + +

Fazit: Investieren Sie in Ihr Fundament

+ + Plugins sind wie billige Anbauwände: Sie wirken im Katalog gut, aber nach + dem ersten Umzug wackeln sie. + + + Lassen wir gemeinsam eine Plattform schaffen, die Ihr Business auch in + fünf Jahren noch zuverlässig trägt. + + + + Qualität ist die einzige Abkürzung, die wirklich funktioniert. + + diff --git a/apps/web/content/blog/maintenance-for-headless-systems.mdx b/apps/web/content/blog/maintenance-for-headless-systems.mdx new file mode 100644 index 0000000..e5914ec --- /dev/null +++ b/apps/web/content/blog/maintenance-for-headless-systems.mdx @@ -0,0 +1,157 @@ +--- +title: "Wartungsfrei durch Verzicht: Warum 'Kein CMS' oft das bessere CMS ist" +description: "Sicherheit und Geschwindigkeit durch architektonische Reduktion: Warum Git-basierte Workflows klassische Admin-Backends schlagen." +date: "2026-02-01" +tags: ["maintenance", "architecture"] +--- + + + Ein CMS wird oft als Befreiung verkauft. In der Realität ist es oft der + Anfang einer teuren Abhängigkeit. + + + In meiner Praxis sehe ich, wie Unternehmen hunderte Stunden in die Pflege + von Systemen investieren, die sie eigentlich entlasten sollten. + + + Ich zeige Ihnen, warum Content-Management ohne Ballast{" "} + der wahre Hebel für Geschwindigkeit und Fokus ist. + + +

Der CMS-Wartungs-Albtraum

+ + Klassische CMS-Lösungen (wie WordPress oder Typo3) sind komplexe + Software-Monster. + + + Sie müssen ständig aktualisiert werden, weil wöchentlich neue + Sicherheitslücken auftauchen. + + + Diese Wartung frisst Zeit und Geld, ohne einen einzigen Cent Mehrwert für + Ihr Business zu generieren. + + + Ich nenne das technische Sisyphusarbeit. + + + Warum ein ganzes Kraftwerk betreiben, wenn Sie nur eine Glühbirne zum + Leuchten bringen wollen? + + + Wahre Effizienz bedeutet, Komplexität radikal zu streichen. + + +
+ +graph TD + Need["Änderung am Inhalt"] --> Path["Update-Prozess"] + Path --> CMS["Klassisches CMS (Update/Backup/DB)"] + Path --> Git["Git-based Workflow (Mintel)"] + CMS --> Risk["Sicherheitslücken & Träge Ladezeit"] + Git --> Speed["Instant Go-Live & Maximale Sicherheit"] + Speed --> Focus["Fokus auf Kunden & Strategie"] + style Git fill:#4ade80,stroke:#333 + style Focus fill:#4ade80,stroke:#333 + +
+ Der schlanke Workflow: Wir eliminieren die Datenbank-Ebene, um + Angriffsflächen zu schließen und das Tempo zu verdoppeln. +
+
+ +

Content as Code: Die Architektur der Profis

+ + Anstatt sich mit unübersichtlichen Admin-Backends herumzuschlagen, + integrieren wir Inhalte direkt in den Deployment-Prozess. + + + Das bedeutet: Wenn Sie etwas ändern wollen, geschieht das in einer + sauberen, versionierten Umgebung. + + + Keine Datenbanken, die korrumpieren können. Kein Backend, das gehackt + werden kann. + + + Das ist Sicherheit durch Simplizität. + +Inhalte werden so stabil wie die Architektur selbst. + +

Warum "Kein CMS" die beste CMS-Strategie ist

+ + Vermeintlich "einfache" Editoren führen oft zu zerstückelten Layouts und + inkonsistentem Design. + + + Durch meinen Code-basierten Ansatz bleibt Ihre Markenidentität zu 100 % + geschützt. + + + + + 0 % Sicherheitsrisiko: Ohne Datenbank-Schnittstelle + gibt es keine Login-Bereiche für Hacker. Ihre Seite ist faktisch immun. + + + Blitzschnelle Änderungen: Wir nutzen automatisierte + Pipelines. Eine Textänderung ist in Sekunden weltweit live. + + + Reduzierte Fixkosten: Sie sparen sich teure + Wartungsverträge für "Backend-Security".{" "} + Geld, das produktiver in Marketing fließen kann. + + + +
+ +
+ +

Fokus auf das, was zählt: Ihre Botschaft

+ + Die wertvollste Ressource in Ihrem Unternehmen ist Aufmerksamkeit. + + + Verschwenden Sie diese nicht mit technischen Nebenschauplätzen. + + + Ich baue Ihnen ein System, das einfach funktioniert – im Hintergrund, + lautlos und effizient. + + + Investieren Sie in Inhaltliche Exzellenz statt in + technische Reparaturen. + +Befreien Sie Ihr Business von der CMS-Last. + +

Wann ist dieser minimalistische Weg für Sie richtig?

+ + Ich arbeite für Entscheider, deren Kerngeschäft nicht das Betreiben einer + IT-Infrastruktur ist. + + + Wollen Sie eine Website, die einfach{" "} + immer online und immer schnell ist? + + + Ich bin der Architekt für alle, die Klarheit und Ergebnisorientierung über + Feature-Listen stellen. + + +

Fazit: Simplizität ist das neue High-End

+Die besten Systeme sind die, die man nicht spürt. + + Lassen wir gemeinsam den Ballast abwerfen und uns auf Ihren Erfolg + konzentrieren. + + + Maximale Wirkung bei minimalem technischem Overhead. Ihr + Erfolg verdient dieses effiziente Fundament. + diff --git a/apps/web/content/blog/no-us-cloud-platforms.mdx b/apps/web/content/blog/no-us-cloud-platforms.mdx new file mode 100644 index 0000000..3846568 --- /dev/null +++ b/apps/web/content/blog/no-us-cloud-platforms.mdx @@ -0,0 +1,156 @@ +--- +title: "Warum ich keine US-Cloud-Plattformen nutze" +description: "Souveränität durch lokale Infrastruktur: Wie Sie sich vor dem Zugriff fremder Behörden schützen." +date: "2026-02-07" +tags: ["cloud", "privacy"] +--- + + + "Die Daten liegen sicher in der Cloud." – Dieser Satz ist heute oft eine + gefährliche Halbwahrheit. + + + In meiner Arbeit als Digital Architect begegne ich unzähligen Unternehmen, + die die Kontrolle über ihre wichtigsten Assets verloren haben. + + + Sie sind abhängig von US-Infrastrukturen und rechtlichen Grauzonen. + + + Ich zeige Ihnen, warum lokale Datenhoheit der wahre Hebel + für Sicherheit und Geschwindigkeit ist. + + +

Das Märchen von der sorglosen US-Cloud

+Die großen Hyper-Scaler bieten Bequemlichkeit. + + Doch diese Bequemlichkeit hat einen Preis: Ihre Souveränität. + + + Durch Gesetze wie den Cloud Act können US-Behörden theoretisch Zugriff auf + Daten verlangen, die auf US-Servern liegen – völlig egal, wo diese + physisch stehen. + + + Für europäische Unternehmen mit sensiblen Kunden- oder Prozessdaten ist + das ein untragbares strategisches Risiko. + + + Wer seine Datenhoheit aufgibt, macht sein Business erpressbar. + + +
+ +graph TD + Data["Ihre geschäftskritischen Daten"] --> Hosting["Strategische Hosting-Wahl"] + Hosting --> US["US Hyper-Scaler (Abhängigkeit)"] + Hosting --> Local["European Local Cloud (Souveränität)"] + US --> Risk["Rechtliche Unsicherheit & Cloud Act"] + Local --> Compliance["DSGVO-Safe & Daten-Immunität"] + Compliance --> Speed["Niedrige Latenz & Absolute Kontrolle"] + style Local fill:#4ade80,stroke:#333 + style Risk fill:#fca5a5,stroke:#333 + +
+ Architektonische Entscheidung: Geopolitische Risiken minimieren durch + bewusste Standort-Wahl. +
+
+ +

Technologie ist niemals neutral

+ + Hinter jedem Server steht eine politische und rechtliche Realität. + + + Indem wir auf spezialisierte europäische Infrastrukturen setzen, gewinnen + wir Immunität gegen fremde Gesetzgebungen. + + + Das ist digitaler Selbstschutz auf höchstem Niveau. + + + Gleichzeitig profitieren wir von extrem niedrigen Latenzen und einer + Performance, die US-Systeme oft nicht liefern können. + + + Nähe zum Kunden bedeutet hier auch messbare Geschwindigkeit. + + +

Mein Ansatz: Die \"High-Fidelity\" Infrastruktur

+ + Ich baue Systeme, die nicht nur technisch brillant, sondern auch + strategisch unangreifbar sind. + + + Lokalität ist bei mir kein Rückschritt, sondern{" "} + Premium-Protection. + + + + + Physische Souveränität: Wir nutzen Rechenzentren unter + europäischem Recht. Ihre Daten verlassen niemals diesen Rechtsraum. + + + Entkoppelte Dienste: Ich vermeide proprietäre + US-Schnittstellen. Wir nutzen offene Standards für maximale + Architektur-Freiheit. + + + Sovereign Operations: Wir kontrollieren jeden Layer – + vom OS bis zur Applikation.{" "} + Keine Blackboxes, keine Hintertüren. + + + +
+ +
+ +

Souveränität als Wettbewerbsvorteil

+ + In einer Welt, die immer instabiler wird, ist \"Daten-Sicherheit am + Standort\" ein echtes Verkaufsargument. + + + Zeigen Sie Ihren Kunden, dass Sie ihre Informationen ernst nehmen. + + + Wer heute proaktiv auf lokale Cloud-Lösungen setzt, spart + sich morgen teure und hektische Migrationswellen. + + + Ich schaffe Ihnen ein digitales Fundament, das politisch und rechtlich + stabil steht. + +Investieren Sie in Immunität, nicht in Abhängigkeit. + +

Wann macht lokale Exzellenz für Sie Sinn?

+ + Suchen Sie nach maximaler Unabhängigkeit von globalen Playern? + + + Ich bin der richtige Partner für Unternehmen, die{" "} + eigene digitale Assets als Kern ihres Erfolgs begreifen. + + + Für Projekte \"ohne Anspruch\" gibt es Massenangebote. Ich baue für die, die + keine Kompromisse machen. + + +

Fazit: Ihr Business, Ihre Regeln

+Holen Sie sich die Kontrolle über Ihre Daten zurück. + + Ich begleite Sie beim Aufbau einer Architektur, die so unabhängig ist wie + Ihre unternehmerische Vision. + + + Lassen wir Ihre Daten dort, wo sie sicher sind:{" "} + In Ihrem Einflussbereich. + diff --git a/apps/web/content/blog/professional-hosting-operations.mdx b/apps/web/content/blog/professional-hosting-operations.mdx new file mode 100644 index 0000000..bcaf2b7 --- /dev/null +++ b/apps/web/content/blog/professional-hosting-operations.mdx @@ -0,0 +1,160 @@ +--- +title: "Professionelles Hosting: Das Fundament digitaler Exzellenz" +description: "Beyond Shared Hosting: Warum Industrial-Grade Operations für Ihre Marke entscheidend sind und wie wir Hochverfügbarkeit garantieren." +date: "2026-01-29" +tags: ["hosting", "operations"] +--- + + + Ein brillanter Webauftritt ist wertlos, wenn er im entscheidenden Moment + nicht erreichbar ist. + + + Hosting ist für mich kein notwendiges Übel, sondern das schlagende Herz + Ihrer digitalen Präsenz. + + + Ich zeige Ihnen, wie Industrial-Grade Operations dafür + sorgen, dass Sie auch bei massiven Traffic-Spitzen ruhig schlafen können. + + +

Das Märchen vom 'Billig-Hosting'

+ + Viele Unternehmen sparen am falschen Ende und wählen Shared-Hosting für + wenige Euro. + + + Der Preis dafür ist eine geteilte Performance und ein erhöhtes + Sicherheitsrisiko. + + + Ist Ihr Nachbar auf dem Server Ziel einer Attacke, geht Ihre Seite mit + unter. + + + Das ist kein vernünftiges Geschäftsmodell, sondern ein digitales + Glücksspiel. + + + In meiner Welt gibt es keine Kompromisse bei der Erreichbarkeit. + + + Ihre Marke verdient eine eigene Umlaufbahn. + + +
+ +graph TD + Deploy["Automatisches Deployment (Git Push)"] --> Build["Isolierte Container-Builds"] + Build --> Global["Global CDN Verteilung"] + Global --> User["Nutzer (Weltweit blitzschnell)"] + Global --> Failover["Automatisches Failover (Sicherheit)"] + style Global fill:#4ade80,stroke:#333 + style Failover fill:#4ade80,stroke:#333 + +
+ Die Cloud-Native Architektur: Skalierung per Knopfdruck und + Ausfallsicherheit durch globale Redundanz. +
+
+ +

Infrastruktur als Code: Die moderne Festung

+ + Ich konfiguriere Server nicht manuell durch Klicken in irgendwelchen + Web-Interfaces. + + + Ich schreibe die Infrastruktur als Code (IaC). Das bedeutet absolute + Reproduzierbarkeit und Fehlerfreiheit. + + + Sollte ein Rechenzentrum ausfallen, "erwacht" Ihr System an einem anderen + Ort in Minuten wieder zum Leben. + + + Das ist das Niveau von Hochverfügbarkeit, das ich für + meine Kunden realisiere. + + + Ihre Website wird zu einem unverwüstlichen digitalen Asset. + + +

Meine Prinzipien für reibungslose Operations

+Was macht eine professionelle Hosting-Strategie aus? +Es sind die unsichtbaren Schutzschilde: + + + + Edge Computing & Caching: Ihre Seite wird direkt dort + ausgeliefert, wo der Nutzer ist. Ob in New York oder Berlin – Ladezeiten + unter 1 Sekunde sind der Standard. + + + Automatisierte Backups & Rollbacks: Mit einem Klick + können wir jede Version der Vergangenheit wiederherstellen.{" "} + Keine Angst vor Fehlern. + + + Echtzeit-Monitoring: Ich sehe Probleme, bevor Ihre + Kunden sie bemerken. Proaktives Handeln ist besser als reaktives + Flicken. + + + +
+ +
+ +

Warum Sorgenfreiheit ein Investment ist

+ + Was kostet Sie eine Stunde Website-Ausfall während einer wichtigen + Kampagne? + + + Meist ist dieser Schaden weitaus höher als die Investition in eine solide + Architektur. + + + Ich schlage die Brücke zwischen Hochleistungstechnologie und + geschäftlichem Erfolg. + + + Lassen Sie uns Ihre Plattform auf ein{" "} + industrielles Fundament stellen. + + + Ruhe im Betrieb ist das Ergebnis von erstklassiger Planung. + + +

Für wen ich 'Unverwüstlichkeit' baue

+ + Ich arbeite für Gründer, die über das Stadium von "es läuft irgendwie" + hinaus sind. + + + Ist Ihre Website das Schaufenster Ihres Erfolgs? Dann sollte sie auf dem + besten Fundament stehen, das die Technik bietet. + + + Ich bin der Architekt für kompromisslose Verfügbarkeit. + + +

Fazit: Ihre Plattform verdient das Beste

+Lassen wir das Basteln im Maschinenraum hinter uns. + + Ich baue Ihnen eine Umgebung, die mit Ihren Ambitionen mitwächst. + + + Sicher. Schnell. Skalierbar. Das ist modernes Hosting auf + Boutique-Niveau. + + + Gönnen Sie Ihrem Business die technologische Souveränität, die es + verdient. + diff --git a/apps/web/content/blog/responsive-design-high-fidelity.mdx b/apps/web/content/blog/responsive-design-high-fidelity.mdx new file mode 100644 index 0000000..f9f0597 --- /dev/null +++ b/apps/web/content/blog/responsive-design-high-fidelity.mdx @@ -0,0 +1,142 @@ +--- +title: "Responsive Design: Exzellenz auf jedem Endgerät" +description: "Mehr als nur Boxen rücken: Warum architektonische Präzision und plattformübergreifende Ergonomie Ihre globale Conversion-Rate steigern." +date: "2026-01-27" +tags: ["design", "ux"] +--- + + + "Responsive" bedeutet heute viel mehr als nur das Nebeneinander-Rücken von + Boxen. + + + In meiner Welt als Digital Architect ist jedes Endgerät eine eigene Bühne + mit eigenen Regeln. + + + Ich zeige Ihnen, warum Responsive-Exzellenz der Hebel für + Ihre globale Conversion-Rate ist. + + +

Jenseits der Standard-Breakpoints

+ + Die meisten Agenturen nutzen simple Raster, die auf dem Desktop gut + aussehen und auf dem Smartphone "irgendwie funktionieren". + + + Das Ergebnis ist oft frustrierend: Zu kleine Texte, unbedienbare Buttons + und Bilder, die das Layout sprengen. + + + Ich betrachte Responsive Design als{" "} + architektonische Präzisionsleistung. + + + Wir gestalten die Experience for den Nutzer im Zug ebenso perfekt wie für + den Entscheider am 4K-Monitor. + + + Fokus und Hierarchie müssen auf jedem Screen neu definiert werden. + + +
+ +graph TD + Logic["Zentrales Design-System"] --> Mobile["Mobile (Daumen-Optimiert)"] + Logic --> Tablet["Tablet (Touch & Swipe)"] + Logic --> Desktop["Desktop (Maus & Tastatur)"] + Mobile --> UX["Perfekte UX auf jedem Gerät"] + Tablet --> UX + Desktop --> UX + style UX fill:#4ade80,stroke:#333 + style Logic fill:#4ade80,stroke:#333 + +
+ Plattformübergreifende Brillanz: Ein System, das sich nicht nur anpasst, + sondern für jedes Endgerät optimiert wird. +
+
+ +

Kontextsensitive Ergonomie

+ + Ein mobiler Nutzer hat andere Bedürfnisse als ein Desktop-Nutzer. + + + In meiner Architektur passen wir nicht nur das Layout an, sondern oft auch + die Interaktionslogik. + + + Das ist digitale Ergonomie auf Boutique-Niveau. + + + Informationen müssen dort fließen, wo sie gebraucht werden – ohne + Reibungsverluste durch das Endgerät. + + +

Meine Hebel für ein grenzenloses Web

+Wie erreichen wir diese technische Perfektion? +Durch den Einsatz modernster Engineering-Methoden: + + + + Fluid Typography & Spacing: Wir nutzen keine starren + Pixel-Werte. Alles atmet und skaliert harmonisch mit der Viewport-Größe. + + + Adaptive Media: Bilder und Videos werden exakt in der + Größe ausgeladen, die das Display erfordert.{" "} + Kein Byte wird verschwendet. + + + Intelligente Touch-Targets: Wir optimieren alle + interaktiven Elemente für die menschliche Anatomie – auf jedem Gerät. + + + +
+ +
+ +

Wirtschaftlicher Hebel: Mobiler Erfolg

+ + Über 60 % des Web-Traffics findet heute auf mobilen Endgeräten statt. + + + Wer hier patzt, verliert die Mehrheit seiner potenziellen Kunden. + + + Ich schaffe Ihnen ein System, das Zukunftssicherheit{" "} + garantiert – egal welche Geräte morgen auf den Markt kommen. + + + Ihre Website wird zu einem universellen Werkzeug Ihres Erfolgs. + + +

Für wen ich 'grenzenlose' Welten baue

+ + Mein Fokus liegt auf Marken mit einem internationalen, mobilen Publikum. + + + Verlangen Ihre Kunden Perfektion in jedem Moment? Dann bin ich Ihr + Architekt. + + + Ich schaffe Barrierefreiheit durch Qualität. + + +

Fazit: Konsistenz ist die halbe Miete

+Ihre Marke muss sich überall gleich wertig anfühlen. + + Lassen wir gemeinsam ein digitales Ökosystem erschaffen, das auf jedem + Screen glänzt. + + + Präzision im Detail, Harmonie im Ganzen. Ihr Erfolg + verdient diese nahtlose Integration. + diff --git a/apps/web/content/blog/slow-loading-costs-customers.mdx b/apps/web/content/blog/slow-loading-costs-customers.mdx new file mode 100644 index 0000000..d8d34f5 --- /dev/null +++ b/apps/web/content/blog/slow-loading-costs-customers.mdx @@ -0,0 +1,179 @@ +--- +title: "Langsame Ladezeiten: Diese technischen Altlasten kosten Sie Kunden" +description: "Wie Sie versteckte Performance-Killer identifizieren und eliminieren, bevor sie Ihren Umsatz gefährden." +date: "2026-02-14" +tags: ["performance", "business"] +--- + + + Zeit ist im modernen Web die härteste Währung. + + + In meiner Laufbahn als Digital Architect habe ich miterlebt, wie + Millisekunden über den Erfolg von Geschäftsmodellen entscheiden. + + + Wer technische Altlasten ignoriert, zahlt Zinsen in Form von massiven + Kundenverlusten. + + + + Bis das digitale Business schließlich an technischer Zahlungsunfähigkeit + scheitert + + . + + +

Zinsen auf schlechte technische Entscheidungen

+ + Technik-Schulden (Technical Debt) entstehen oft schleichend. + + + Man wählt heute die schnelle, unsaubere Lösung, um vermeintlich Zeit zu + sparen. + + + Doch diese Entscheidung ist ein digitaler Kredit mit extrem hohen Zinsen. + + + Bei der Performance äußert sich das in trägen Ladezeiten, die sich wie + Blei auf Ihre Conversion-Rate legen. + + + Jede Sekunde Verzögerung senkt die Abschlussquote im Schnitt um 20 %. + + + Das ist kein bloßes IT-Thema – das ist ein{" "} + massives betriebswirtschaftliches Risiko. + + + Ich helfe Ihnen, diese Performance-Killer aufzuspüren und systematisch zu + eliminieren. + + + Oft sind es kleine Versäumnisse, die in der Summe ein System unrettbar + schwerfällig machen. + + +
+ +graph LR + Slow["Träge Website"] --> Frust["Nutzer-Frustration (Subliminal)"] + Frust --> Bounce["Hohe Absprungrate"] + Bounce --> Rank["Ranking-Verlust bei Google"] + Rank --> Loss["Umsatz- & Reputationsverlust"] + Loss --> Debt["Explodierende Akquisekosten"] + style Loss fill:#ef4444,color:#fff + style Debt fill:#ef4444,color:#fff + +
+ Der fatale Teufelskreis der Ladezeit: Technische Schulden führen zu + immer höheren Opportunitätskosten. +
+
+ +

Die Psychologie des digitalen Wartens

+ + Wussten Sie, dass die menschliche Wahrnehmung von Zeit im Internet völlig + verzerrt ist? + + + Eine Sekunde Wartezeit fühlt sich digital wie eine kleine Ewigkeit an. + + + Wenn eine Seite nicht sofort reagiert, sendet dies ein Signal von{" "} + Unzuverlässigkeit und Inkompetenz. + + + Der Nutzer fragt sich unbewusst: "Wenn sie schon ihre Website nicht im + Griff haben, wie gehen sie dann mit meinen Daten um?" + + + Ich sorge für eine "Instant-Feel" Erfahrung durch intelligentes + Pre-Loading. + + + Das schafft Vertrauen, noch bevor das erste Wort gelesen wurde. + + +

Wo die Schulden in Ihrem System wirklich lauern

+ + In meiner Analyse begegnen mir immer wieder die gleichen drei Quellen für + technische Altlasten. + + + Diese zu bereinigen ist der Hebel für Ihre digitale Rendite: + + + + Legacy Script Bloat: Veraltete Tracker, die den Browser + bei jedem Aufruf blockieren. Wir entfernen den Ballast radikal. + + + Architektonische Trägheit: Dynamische Abfragen, wo + statische Antworten möglich wären. Wir liefern Antworten in + Lichtgeschwindigkeit. + + + Die mobile Vernachlässigung: Eine Seite, die im + 4G-Funkloch zur Qual wird.{" "} + Hier findet oft der Erstkontakt statt. + + + +
+ +
+ +

Refactoring als Rendite-Beschleuniger

+ + Ich flicke nicht an Symptomen herum, ich optimiere die DNA Ihres Systems. + + + Ein strategisches Refactoring ist der mutige Schritt weg von + schwerfälligen Datenbank-Monolithen. + + + Dies ist kein technisches Selbstverwirklichungsprojekt, sondern eine + Investition mit knallhartem ROI. + + + Wer schneller ist als der Wettbewerb, gewinnt die Aufmerksamkeit zuerst. + + + In einem gesättigten Markt ist{" "} + Geschwindigkeit ein entscheidender Wettbewerbsvorteil. + + +

Wann ist es Zeit für eine Schuldenbereinigung?

+ + Wenn Sie spüren, dass Ihre aktuelle Technik Ihre Ambitionen ausbremst, bin + ich Ihr Architekt. + + + Ich verstehe, dass für junge Projekte ein pragmatischer Ansatz oft + sinnvoll ist. + + + Wenn Sie jedoch an einem Punkt stehen, an dem Professionalität und + Skalierung zählen, gießen wir das{" "} + technologische Fundament. + + +

Fazit: Befreien Sie Ihr Business

+ + Technische Schulden sind eine unsichtbare Bremse für Ihr Wachstum. + + + Ich baue Ihnen ein System, das durch{" "} + Effizienz und Klarheit überzeugt. + + + Ihr Business verdient es, unbeschwert und frei von Altlasten zu skalieren. + diff --git a/apps/web/content/blog/test-mermaid.mdx b/apps/web/content/blog/test-mermaid.mdx new file mode 100644 index 0000000..74eb9ef --- /dev/null +++ b/apps/web/content/blog/test-mermaid.mdx @@ -0,0 +1,22 @@ +--- +title: "Test Mermaid" +description: "Testing Mermaid rendering" +date: "2026-01-01" +tags: ["test"] +--- + +Test 1: Plain string attribute (NO quotes in graph content): + + +graph TD + A-->B + B-->C + + +Test 2: Children as raw text (no template literal): + + +graph TD + D-->E + E-->F + diff --git a/apps/web/content/blog/website-without-cookie-banners.mdx b/apps/web/content/blog/website-without-cookie-banners.mdx new file mode 100644 index 0000000..24ff78d --- /dev/null +++ b/apps/web/content/blog/website-without-cookie-banners.mdx @@ -0,0 +1,171 @@ +--- +title: "Websites ohne Cookie-Banner: Ein Haltungs-Statement" +description: "Warum Cookie-Banner ein Zeichen für schlechtes Engineering sind und wie Sie UX und Datenschutz brillant vereinen." +date: "2026-02-06" +tags: ["privacy", "ux"] +--- + + + Ich halte Cookie-Banner für eine der größten Design-Sünden und + Vertrauenskiller des modernen Webs. + + + Sie stören den Lesefluss und suggerieren eine Pseudo-Sicherheit. + + + Vor allem signalisieren sie eines: Ein mangelhaftes technisches Konzept. + + + Ich zeige Ihnen, wie wir{" "} + vollständig ohne Banner auskommen – bei 100 % + DSGVO-Konformität. + + +

Das Banner-Paradoxon: Warum wir uns das antun

+ + Klassische Websites laden oft ungefragt Scripte von Drittanbietern – meist + US-Konzerne. + + + Diese Tracker setzen Cookies, um Nutzer über verschiedene Seiten hinweg zu + verfolgen. + + + Das Ergebnis ist das tägliche Banner-Chaos, das den Inhalt überdeckt. + + + Viele Besucher klicken frustriert auf "Alle akzeptieren" oder verlassen + die Seite sofort wieder. + + + Ich verfolge eine radikal andere Philosophie:{" "} + + Wenn wir keine Daten abfließen lassen, brauchen wir auch keine + Erlaubnis. + + + + Es ist eine Frage der technischen Souveränität und des digitalen Anstands. + + +
+ +graph TD + User["Nutzer besucht Website"] --> Logic["Mintel Privacy Engine"] + Logic --> Assets["Lokale Assets (Fonts/Scripts)"] + Logic --> Analytics["Aggregierte, anonyme Metriken"] + Assets --> NoBanner["Kein Cookie-Banner nötig"] + Analytics --> NoBanner + NoBanner --> Experience["Sofortige Experience & Vertrauen"] + style NoBanner fill:#4ade80,stroke:#333 + style Experience fill:#4ade80,stroke:#333 + +
+ Privacy by Design: Wenn die Architektur den Schutz bereits garantiert, + entfallen die rechtlichen Krücken. +
+
+ +

Die unsichtbare Transaktion des Vertrauens

+ + Jedes Mal, wenn ein Nutzer Ihre Seite ohne Banner betreten kann, findet + eine unsichtbare Transaktion statt: Vertrauensaufbau. + + + Sie signalisieren Ihrem Besucher: "Ich brauche deine persönlichen Daten + nicht, um dich zu überzeugen." + + + Privatsphäre ist heute ein High-End Feature. + + + Ich baue Systeme, die diesen Respekt technisch erzwingen und so die + Markenbindung stärken. + + + Wir verzichten konsequent auf invasive Tracker und gewinnen dafür loyale + Nutzer. + + +

Mein Weg zu 100 % technischer Souveränität

+ + Privacy-first bedeutet für mich nicht Verzicht, sondern intelligenteres + Engineering. + + + Wir messen Ihren Erfolg – aber wir brauchen keine personenbezogenen + Profile. + + + + + Full Local Hosting: Google Fonts und sämtliche Scripte + liegen direkt auf Ihrer Infrastruktur. Kein Datentransfer zu US-Servern. + + + Ethische Telemetrie: Analysetools, die Nutzerwege + messen, ohne Cookies zu setzen.{" "} + Volle Transparenz, ohne Verfolgung. + + + Zero-Tracking Default: Mein Framework ist so ausgelegt, + dass "Consent-Einforderung" technisch gar nicht notwendig wird. + + + +
+ +
+ +

Der ökonomische Vorteil von 'Banner-Freiheit'

+ + Consent-Management-Tools verschlechtern oft selbst die Ladezeit Ihrer + Website. + + + Sie laden schwere JavaScript-Dateien, noch bevor Ihre Botschaft erscheint. + + + Indem wir diese Tools eliminieren, verbessern wir die UX und Ihre + PageSpeed-Werte. + + + Zudem vermeiden Sie Abo-Kosten und das Risiko von Abmahnungen durch + falsche Skripte. + + + Ein sauberes System ist wartungsarm und rechtssicher. + + +

Wann macht dieser radikale Ansatz für Sie Sinn?

+ + Ich verstehe mich als Partner für Marken, die Haltung zeigen. + + + Wenn Datenschutz für Sie ein echtes Qualitätsmerkmal ist, ist dies mein + absoluter Standard. + + + Ich arbeite für Unternehmen, die{" "} + durch Überzeugung und Relevanz verkaufen, nicht durch + Verfolgung. + + +

Fazit: Befreien Sie Ihre Inhalte

+ + Eine Website ohne Banner wirkt sofort aufgeräumter, ehrlicher und + wertiger. + + + Es ist ein klares Statement für digitale Professionalität und Respekt. + + + Ich baue Ihnen die Brücke in eine{" "} + bannerfreie, souveräne Zukunft. + diff --git a/apps/web/content/blog/why-agencies-are-slow.mdx b/apps/web/content/blog/why-agencies-are-slow.mdx new file mode 100644 index 0000000..76b7563 --- /dev/null +++ b/apps/web/content/blog/why-agencies-are-slow.mdx @@ -0,0 +1,207 @@ +--- +title: "Warum Ihre Agentur für kleine Änderungen Wochen braucht" +description: "Starre Prozesse vs. flexible Architektur: So brechen Sie den Flaschenhals in Ihrer Entwicklung auf." +date: "2026-02-13" +tags: ["architecture", "engineering"] +--- + + + Haben Sie sich schon einmal gefragt, warum eine einfache Textänderung bei + Ihrer Agentur oft zwei Wochen dauert? + + + Ich kenne die Antwort aus meiner jahrelangen Erfahrung: Es liegt an + veralteten Hierarchien und einem technologischen Overhead, der Innovation + im Keim erstickt. + + + Mein Modell radikaler Direktheit beschleunigt Ihr + Business, indem es unnötige Schnittstellen eliminiert. Wir lenken den + Fokus zurück auf das fertige Produkt. + + +

Die "stille Post" der Agentur-Hierarchien

+ + In einer klassischen Full-Service-Agentur landet Ihr Wunsch zuerst beim + Account Manager. + + + Dieser gibt ihn an den Projektleiter weiter, der ein Ticket erstellt, + welches schließlich einem oft überarbeiteten Junior-Entwickler zugewiesen + wird. + + + Jede dieser Stationen ist nicht nur ein potenzieller Flaschenhals, sondern + auch eine Fehlerquelle. + + + Informationen gehen verloren, die Umsetzung dauert ewig und am Ende + bezahlen Sie für Meetings und Ticket-Management statt für Output. + + + Ich nenne das die "Agentur-Steuer". + + + Während dort noch über Prioritäten diskutiert wird, könnte Ihre neue + Lösung schon live sein und ersten Umsatz für Ihr Unternehmen generieren. + + +
+ +graph LR + Request["Ihr Wunsch"] --> AM["Account Manager"] + AM --> PM["Projektleiter (Meetings)"] + PM --> Ticket["Veraltetes Ticketsystem"] + Ticket --> Dev["Junior Entwickler"] + Dev --> Review["Code Review (Wartezeit)"] + Review --> QA["Manuelle QA"] + QA --> Approval["Kundenseitige Freigabe"] + Approval --> Prod["Mühsamer Live-Gang"] + style AM fill:#fca5a5,stroke:#333 + style PM fill:#fca5a5,stroke:#333 + style Ticket fill:#fca5a5,stroke:#333 + +
+ Die traditionelle 'Stille Post': Jede Schnittstelle kostet Sie Zeit, + Präzision und bares Geld. +
+
+ +

Der systemische Interessenkonflikt

+ + Es gibt einen tieferen Grund für die Trägheit vieler Agenturen: Das + Geschäftsmodell. + + + Wenn Agenturen nach abrechenbaren Stunden arbeiten und große Teams + finanzieren müssen, fehlt oft der ökonomische Anreiz zur radikalen + Vereinfachung. + + + Langsame Prozesse bedeuten oft mehr abrechenbare Projektmanagement-Zeit. + + + Ich hingegen habe mein gesamtes Business auf{" "} + Effizienz und technologische Souveränität optimiert. + + + Mein Ziel ist die Maximierung Ihres Outputs, nicht die der Stunden. + + + Ich investiere massiv in meine eigene Toolchain, um Aufgaben, die + Agenturen Tage kosten, in Minuten zu erledigen. + + +

Handwerk statt Fließband: Mein Boutique-Ansatz

+ + In meiner Welt gibt es keine Junioren, an die Arbeit "durchgereicht" wird. + + + Wenn Sie mit mir arbeiten, sprechen Sie direkt mit dem Experten, der die + Architektur entwirft und den Code schreibt. + + + Ich betrachte Softwareentwicklung nicht als anonymen Fließband-Job, + sondern als digitales Kunsthandwerk. + + + Dieser Boutique-Ansatz erlaubt es mir, auf eine Weise flexibel zu sein, + die für große, starre Strukturen unmöglich ist. + + + Ich kann Ideen sofort auf ihre technische Machbarkeit prüfen und + prototypisieren, ohne erst eine interne Freigabewelle abwarten zu müssen. + + +
+ +
+ +

Mein Modell: Radikale Direktheit

+ + Ich nutze Technologien, die darauf ausgelegt sind, Reibung zu eliminieren. + Statt mühsam "Tickets" zu schreiben, bauen wir sofort an Lösungen. + + + Hier sind die drei Hebel, mit denen ich das Tempo Ihres Projekts + verdopple: + + + + Preview-Deployments in Echtzeit: Jede Änderung wird + sofort auf einer geheimen URL visualisiert. Sie sehen den Fortschritt + live. + + + Komponentengestützte Evolution: Ich baue ein + Design-System für Sie, das mitwächst. Neue Seiten entstehen in Stunden. + + + Automatisierte Schutzschilde: Statt fehleranfälliger + manueller QA nutze ich automatisierte Tests.{" "} + Technik schützt Technik. + + + + + +

Für wen ich die Bremse löse

+ + Mein Angebot richtet sich an Gründer und Entscheider, die{" "} + Ergebnisse über Hochglanz-Reports stellen. + + + Suchen Sie einen Partner, der so schnell denkt wie Ihr Business es + erfordert? Dann passen wir zusammen. + + + Unternehmen, die primär nach maximaler personeller Skalierung suchen, sind + bei klassischen Großagenturen besser aufgehoben. + + + Ich arbeite für die Macher, die{" "} + Qualität und Geschwindigkeit durch Intelligenz erreichen + wollen. + + +

Fazit: Ihre Zeit ist ein knappes Gut

+ + Technologische Exzellenz bedeutet für mich auch, Ihnen keine Zeit zu + stehlen. Ich baue Systeme, die fließen. + + + Wenn Sie heute eine Vision haben, möchte ich, dass wir sie übermorgen + bereits am Markt testen können. + + + Lassen Sie uns die Agentur-Steuer streichen und direkt in Ihren Erfolg + investieren. + + + Echte Geschwindigkeit beginnt dort, wo Hierarchie endet. + diff --git a/apps/web/content/blog/why-no-templates-matter.mdx b/apps/web/content/blog/why-no-templates-matter.mdx new file mode 100644 index 0000000..c32a7fb --- /dev/null +++ b/apps/web/content/blog/why-no-templates-matter.mdx @@ -0,0 +1,142 @@ +--- +title: "Warum Templates Ihre Marken-Identität verwässern" +description: "Fast-Food vs. Sterneküche: Warum wir auf Standard-Vorlagen verzichten und jedes Projekt als digitales Unikat für maximale Unterscheidbarkeit bauen." +date: "2026-01-28" +tags: ["design", "strategy"] +--- + + + Vorlagen sind die Fast-Food-Lösung des Web-Designs: Schnell verfügbar, + aber auf Dauer ungesund für Ihre Marke. + + + In meiner Arbeit als Digital Architect begegne ich ständig Unternehmen, + die in der Beliebigkeit von Standard-Templates versinken. + + + Ich zeige Ihnen, warum Zero-Template-Architektur der + einzige Weg zu echter digitaler Distinktion ist. + + +

Die Falle der visuellen Gleichschaltung

+ + Wenn Sie ein Template nutzen, nutzen Sie die gleiche Basis wie tausende + andere Unternehmen weltweit. + + + Das Ergebnis ist eine "digitale Uniform", die Ihre Einzigartigkeit im Keim + erstickt. + + + Kunden spüren unbewusst, wenn eine Seite "von der Stange" kommt. + + + Es signalisiert fehlende Investitionsbereitschaft und mangelnde Vision. + + + Ich nenne das ästhetische Kapitulation. + + + Wahre Markenbildung braucht Raum zum Atmen und ein Fundament, das nur für + Sie gegossen wurde. + + +
+ +graph TD + Need["Individuelle Marken-Botschaft"] --> Path["Design-Entscheidung"] + Path --> Temp["Fertig-Template (Masse)"] + Path --> Custom["Bespoke Component Design (Mintel)"] + Temp --> Bland["Visuelle Beliebigkeit & Hoher Ballast"] + Custom --> Distinct["Maximale Unterscheidbarkeit & Pure Performance"] + Bland --> NoTrust["Vertrauensverlust"] + Distinct --> Authority["Marken-Autorität"] + style Custom fill:#4ade80,stroke:#333 + style Authority fill:#4ade80,stroke:#333 + +
+ Bespoke vs. Template: Investieren Sie in ein digitales Unikat, das Ihre + Marktposition untermauert statt sie zu verwässern. +
+
+ +

Boutique-Design: Jedes Pixel hat einen Zweck

+ + Templates enthalten Code für hunderte Optionen, die Sie nie nutzen werden. + + + Dieser Ballast verlangsamt Ihre Seite und verwässert Ihre Botschaft. + + + In meinem Boutique-Ansatz entwickeln wir jede Komponente von Grund auf. + + + Das Ergebnis ist eine hochpräzise Maschine, die exakt auf + Ihre Ziele ausgerichtet ist. + +Schlank. Schnell. Unverwechselbar. + +

Warum technisches Maßwerk rentabler ist

+ + Templates are starr. Wenn Ihr Business wächst, wird das Template schnell + zur Wachstumsbremse. + + + Anpassungen am Standard-Code sind oft teurer als ein kompletter Neubau. + + + Ich baue Systeme, die evolutionär sind. + + + + + Unbegrenzte Design-Freiheit: Wir biegen den Code nach + Ihrer Vision, nicht umgekehrt. Jede Interaktion folgt Ihrer Marken-DNA. + + + Zukunftssicheres Asset: Da wir keine + Drittanbieter-Themen nutzen, gibt es keine Abhängigkeiten von deren + Update-Zyklen. + + + Maximale Konversions-Rate: Wir optimieren Layouts exakt + nach dem Nutzerverhalten Ihres Zielmarktes.{" "} + Keine Kompromisse. + + + +
+ +
+ +

Wann ist 'Bespoke' der einzige Weg?

+ + Ist Ihr Unternehmen Marktführer oder auf dem Weg dorthin? Dann darf Ihre + Website nicht nach "Durchschnitt" aussehen. + + + Ich arbeite für Entscheider, die{" "} + Qualität als Wettbewerbsvorteil begreifen. + + + Wenn Differenzierung über Ihren Erfolg entscheidet, bin ich Ihr Architekt. + + +

Fazit: Ein Unikat für Ihren Erfolg

+ + Hören Sie auf, sich in fremde Formen pressen zu lassen. + + + Lassen wir gemeinsam ein digitales Denkmal setzen, das so unverwechselbar + ist wie Ihr unternehmerischer Fingerabdruck. + + + Pures Handwerk. Maximale Distinktion. Ihr Erfolg verdient + dieses Niveau. + diff --git a/apps/web/content/blog/why-pagespeed-fails.mdx b/apps/web/content/blog/why-pagespeed-fails.mdx new file mode 100644 index 0000000..d8dd7ec --- /dev/null +++ b/apps/web/content/blog/why-pagespeed-fails.mdx @@ -0,0 +1,191 @@ +--- +title: "Warum Ihre Website bei Google PageSpeed scheitert" +description: "Millisekunden entscheiden über Ihren Umsatz: So optimieren Sie Ihre Web-Performance für maximale Conversion." +date: "2026-02-15" +tags: ["performance", "seo"] +--- + + + Unternehmen investieren oft Unsummen in glänzende Oberflächen, während das + technische Fundament einer digitalen Ruine gleicht. + + + Wenn Ihre Website bei Google PageSpeed scheitert, verlieren Sie Kunden –{" "} + bevor diese Ihre Botschaft überhaupt wahrnehmen können. + + + In meiner Arbeit als Digital Architect ist die Geschwindigkeit der + architektonische Gradmesser für Professionalität. + + +

Der unsichtbare Umsatz-Verschleiß

+ + Stellen Sie sich vor, Sie eröffnen ein Luxus-Geschäft in der besten Lage, + aber die Eingangstür klemmt massiv. + + + Kunden müssen 10 Sekunden lang drücken, um einzutreten. Genau das passiert + täglich auf tausenden Websites. + + + Millisekunden sind im digitalen Zeitalter die härteste Währung. + + + Eine Verzögerung von nur einer Sekunde kann die{" "} + Conversion-Rate um bis zu 20 % senken. Das ist kein + technisches Detail, sondern ein unternehmerisches Risiko. + + + Ich betrachte Performance nicht als IT-Kennzahl, sondern als ökonomischen + Hebel. + + + Google bewertet Websites heute primär nach den "Core Web Vitals". Das sind + präzise Messgrößen für die Frustrationstoleranz Ihrer Nutzer. + + + Wer hier rote Zahlen schreibt, wird vom Algorithmus unsichtbar gemacht – + eine digitale Strafe für technische Nachlässigkeit. + + +
+ +
+ +

Warum klassische Lösungen scheitern

+ + Die Ursache liegt oft in der Verwendung von "All-in-One"-Lösungen wie + WordPress oder überladenen Baukästen. + + + Diese Systeme versuchen, alles für jeden zu sein. Das Ergebnis ist ein + gigantischer "Ballast an Code". + + + Jedes Byte muss durch das Nadelöhr der Internetverbindung gepresst werden, + bevor das erste Bild erscheint. + + + In einer mobilen Welt mit oft instabilen Verbindungen ist das ein{" "} + architektonisches Todesurteil. Wer hier spart, zahlt + später doppelt durch verlorene Kunden. + + +
+ +graph TD + A["Anfrage des Browsers"] --> B["Server muss nachdenken (PHP/DB)"] + B --> C["Hunderte Datenbank-Abfragen"] + C --> D["HTML wird mühsam live konstruiert"] + D --> E["Veraltetes Asset-Management lädt alles"] + E --> F["Render-Blocking Code (Browser stoppt)"] + F --> G["Seite endlich sichtbar (nach 3-5 Sek)"] + style B fill:#fca5a5,stroke:#333 + style F fill:#fca5a5,stroke:#333 + style G fill:#fca5a5,stroke:#333 + +
+ Der Flaschenhals der Standard-Systeme: Rechenzeit am Server raubt Ihnen + wertvolle Kundenzeit. +
+
+ +

Meine Architektur der Geschwindigkeit

+ + Ich verfolge einen radikal anderen Ansatz. Statt die Seite erst mühsam + zusammenzubauen, wenn der Kunde sie anfragt, liefere ich fertig optimierte + "digitale Objekte" aus. + + + Mein "Static-First" Framework sorgt dafür, dass die Antwortzeit Ihres + Servers nahezu bei Null liegt. + + + Völlig egal, ob gerade 10 oder 10.000 Menschen gleichzeitig auf Ihre Seite + zugreifen. + + + Das ist Skalierbarkeit durch Design, nicht durch bloße + Server-Power. + + +

Die drei Säulen meiner Umsetzung

+ + + Zero-Computation am Edge: Durch Static Site Generation + (SSG) liegen alle Inhalte fertig auf globalen CDNs. Keine Wartezeit. + + + Präzises Asset-Engineering: Ich nutze Tree-Shaking. Ihr + Kunde lädt exakt nur den Code, den er wirklich benötigt. + + + Next-Gen Media-Handling: Bilder werden automatisch in + Formaten wie AVIF ausgeliefert. Qualität bleibt, Dateigröße schmilzt. + + + +
+ +pie + "JavaScript Execution" : 35 + "Render Blocking CSS" : 25 + "Server Response Time" : 20 + "Image Loading" : 15 + "Third-Party Scripts" : 5 + +
+ Wo die Zeit wirklich verloren geht: Eine Analyse der häufigsten Ladezeit-Killer. +
+
+ +

Der wirtschaftliche Case

+ + Baukästen wirken "auf den ersten Blick" günstiger. Doch das ist eine + riskante Milchmädchenrechnung. + + + Wenn Sie monatlich 5.000 € in Marketing investieren, aber 30 % Ihrer Leads + durch Ladezeiten verlieren, verbrennen Sie jedes Jahr 18.000 €. + + + Mein System ist kein Kostenfaktor, sondern ein{" "} + ROI-Beschleuniger. + + + Wir senken die Kosten pro Lead, indem wir die Reibungsverluste minimieren. + Ein technisch überlegenes System ist immer die rentablere Wahl. + + +

Wann meine Architektur für Sie Sinn macht

+ + Ich bin Partner für Unternehmen, die über die "digitale Visitenkarte" + hinausgewachsen sind. + + + Ist Ihre Website ein geschäftskritisches Werkzeug für die Lead-Gen? Dann + ist mein Ansatz alternativlos. + + + Ich steige dort ein, wo technologische{" "} + Exzellenz zum entscheidenden Wettbewerbsvorteil wird. + + +

Fazit: Respekt vor der Zeit Ihrer Nutzer

+ + Geschwindigkeit ist letztlich Ausdruck von Wertschätzung. Sie + signalisieren Ihrem Kunden: "Ich respektiere deine Zeit." + + + Lassen Sie uns Ihre Website in eine hochpräzise Wachstums-Maschine + verwandeln. + + + Qualität zahlt sich aus – in Millisekunden und in Euro. + diff --git a/apps/web/content/blog/why-websites-break-after-updates.mdx b/apps/web/content/blog/why-websites-break-after-updates.mdx new file mode 100644 index 0000000..bf86d99 --- /dev/null +++ b/apps/web/content/blog/why-websites-break-after-updates.mdx @@ -0,0 +1,171 @@ +--- +title: "Warum Ihre Website nach Updates nicht mehr funktioniert" +description: "Stabilität durch Engineering: So beenden Sie den Teufelskreis aus Updates und Layout-Fehlern." +date: "2026-02-11" +tags: ["maintenance", "reliability"] +--- + + + "Nach dem letzten Update war plötzlich das halbe Layout kaputt." + + + Das ist der Satz, den ich am häufigsten von Neukunden höre. + + + Für mich ist eine Website ein technisches Präzisionswerkzeug. Es darf + niemals einfach "auseinanderfallen". + + + Stabilität ist kein glücklicher Zufall, sondern das + Ergebnis eines kompromisslosen Engineering-Systems. + + +

Die Entropie des Webs

+Das Internet ist eine extrem dynamische Umgebung. + + Browser-Updates und neue Sicherheitsstandards nagen permanent an der + Integrität Ihrer Website. + + + In herkömmlichen Systemen sind die Komponenten oft wie ein wackeliger + Stapel Lego-Steine angeordnet. + + + Zieht man einen Stein heraus – etwa durch ein Plugin-Update –, gerät das + gesamte Konstrukt ins Wanken. + +Ich nenne das den schleichenden "Software-Zerfall". + + Ich baue Architektur, die diesem Zerfall aktiv widersteht + . + + +
+ +graph TD + Update["Technische Änderung / Update"] --> Test["Automatisierte Qualitäts-Tests"] + Test -->|OK| Deploy["Automatischer, sicherer Live-Gang"] + Test -->|Fehler| Alert["Sofortiger Stopp & Fehler-Isolation"] + Alert --> Fix["Manuelle Korrektur durch mich (Dev)"] + Deploy --> Stable["Website bleibt 100% konsistent"] + style Stable fill:#4ade80,stroke:#333 + style Alert fill:#ef4444,color:#fff + style Deploy fill:#4ade80,stroke:#333 + +
+ Mein defensives Sicherheitsnetz: Keine Änderung erreicht den Nutzer, + ohne maschinell zertifiziert zu sein. +
+
+ +

Die Kosten des menschlichen Versagens eliminieren

+ + Die meisten Fehler entstehen durch manuelle Eingriffe oder das Übersehen + von Seiteneffekten. + + + Ein Entwickler ändert das Design auf einer Unterseite und merkt nicht, + dass dadurch das Kontaktformular bricht. + + + In meiner Welt gibt es solche Fehler nicht. Ich investiere in{" "} + automatisierte Wächtern. + + + Bevor eine Änderung live geht, prüft eine künstliche Instanz jedes Detail + Ihrer gesamten Website. + + + Technik schützt hier den Menschen vor Flüchtigkeitsfehlern. + + +

Die "Fortress-Mentalität": Drei Schichten der Sicherheit

+ + Sorgen Sie sich nie wieder darum, ob Ihre Seite "das Wochenende überlebt + hat". + + + Mein Stabilitäts-System umfasst drei entscheidende Schutzschichten: + + + + + Visual Regression Testing: Mein System vergleicht nach + jeder Änderung tausende Bildpunkte. Die Maschine sieht Fehler sofort. + + + Immutable Deployments: Ich überschreibe niemals + Live-Dateien. Wir können in Millisekunden auf eine saubere Kopie + zurückrollen. + + + Entkoppelte Modul-Logik: Ich baue in isolierten + Komponenten. Änderung an Punkt A gefährden niemals Punkt B. + + + +
+ +stateDiagram-v2 + ["*"] --> Development + Development --> Testing : Code Complete + Testing --> Staging : Tests Pass + Staging --> Production : Final Approval + Production --> Rollback : Issue Detected + Rollback --> Development : Fix Required + Testing --> Development : Tests Fail + Production --> ["*"] + +
+ Jeder Zustand ist abgesichert: Keine Änderung erreicht Production ohne vollständige Validierung. +
+
+ +
+ +
+ +

Warum Stabilität die beste Wachstums-Strategie ist

+ + Ein stabiles System ist kein technischer Selbstzweck. Es ist die + Grundvoraussetzung für Skalierung. + + + Nur wenn Sie blind auf Ihr technologisches Rückgrat vertrauen können, + können Sie mit voller Kraft investieren. + + + Ich baue keine "Schönwetter-Websites", sondern{" "} + industrielle Software-Systeme. + +Stabilität bedeutet für Sie Fokus auf Ihr Business. + +

Für wen ist meine 'Fortress-Architektur' richtig?

+ + Gefährden optische Defekte oder Ausfälle direkt Ihren Ruf? Dann brauchen + Sie eine ernsthafte Architektur. + + + Ich werde aktiv, wenn{" "} + Professionalität und Zuverlässigkeit für Sie nicht + verhandelbar sind. + + +

Fazit: Ihre digitale Ruhe ist mein Auftrag

+ + Souveränität im Netz beginnt bei der Verlässlichkeit der eigenen + Werkzeuge. + + + Lassen wir die Zeit der "kaputten Layouts" ein für alle Mal beenden. + + + Stabilität ist die Basis für Vertrauen. Ihr Erfolg + verdient dieses solide Fundament. + diff --git a/apps/web/contentlayer.config.ts b/apps/web/contentlayer.config.ts new file mode 100644 index 0000000..993105c --- /dev/null +++ b/apps/web/contentlayer.config.ts @@ -0,0 +1,28 @@ +import { defineDocumentType, makeSource } from 'contentlayer2/source-files' + +export const Post = defineDocumentType(() => ({ + name: 'Post', + filePathPattern: `blog/**/*.mdx`, + contentType: 'mdx', + fields: { + title: { type: 'string', required: true }, + date: { type: 'string', required: true }, + description: { type: 'string', required: true }, + tags: { type: 'list', of: { type: 'string' }, required: true }, + }, + computedFields: { + slug: { + type: 'string', + resolve: (doc) => doc._raw.sourceFileName.replace(/\.mdx$/, ''), + }, + url: { + type: 'string', + resolve: (post) => `/blog/${post._raw.sourceFileName.replace(/\.mdx$/, '')}`, + }, + }, +})) + +export default makeSource({ + contentDirPath: 'content', + documentTypes: [Post], +}) diff --git a/apps/web/mdx-components.tsx b/apps/web/mdx-components.tsx new file mode 100644 index 0000000..f0360e6 --- /dev/null +++ b/apps/web/mdx-components.tsx @@ -0,0 +1,11 @@ +// import type { MDXComponents } from 'mdx/types'; // Commented out due to resolution issue +import { mdxComponents as registryComponents } from './src/content-engine/registry'; + +export const MDXComponents = registryComponents; + +export function useMDXComponents(components: any): any { + return { + ...components, + ...registryComponents, + }; +} diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs index 6e70a14..c0ae313 100644 --- a/apps/web/next.config.mjs +++ b/apps/web/next.config.mjs @@ -1,7 +1,11 @@ +import { withContentlayer } from 'next-contentlayer2'; import withMintelConfig from "@mintel/next-config"; +import createMDX from '@next/mdx'; + /** @type {import('next').NextConfig} */ const nextConfig = { + pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'], reactStrictMode: true, output: 'standalone', images: { @@ -25,4 +29,8 @@ const nextConfig = { }, }; -export default withMintelConfig(nextConfig); +const withMDX = createMDX({ + // Add markdown plugins here, as desired +}); + +export default withContentlayer(withMintelConfig(withMDX(nextConfig))); diff --git a/apps/web/package.json b/apps/web/package.json index c24d751..f73ffff 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -30,8 +30,13 @@ }, "dependencies": { "@directus/sdk": "21.0.0", + "@mdx-js/loader": "^3.1.1", + "@mdx-js/react": "^3.1.1", "@mintel/cloner": "^1.8.0", + "@mintel/content-engine": "link:../../../at-mintel/packages/content-engine", + "@mintel/meme-generator": "link:../../../at-mintel/packages/meme-generator", "@mintel/pdf": "^1.8.0", + "@next/mdx": "^16.1.6", "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^2.1.0", "@opentelemetry/core": "^2.1.0", @@ -52,12 +57,16 @@ "axios": "^1.13.4", "canvas-confetti": "^1.9.4", "clsx": "^2.1.1", + "contentlayer2": "^0.5.8", "crawlee": "^3.15.3", + "esbuild": "^0.27.3", "framer-motion": "^12.29.2", "ioredis": "^5.9.1", "lucide-react": "^0.468.0", "mermaid": "^11.12.2", "next": "^16.1.6", + "next-contentlayer2": "^0.5.8", + "next-mdx-remote": "^6.0.0", "nodemailer": "^8.0.1", "playwright": "^1.58.1", "prismjs": "^1.30.0", @@ -100,4 +109,4 @@ "typescript": "5.9.3", "typescript-eslint": "^8.54.0" } -} +} \ No newline at end of file diff --git a/apps/web/scripts/clean-mermaid-format.ts b/apps/web/scripts/clean-mermaid-format.ts new file mode 100644 index 0000000..5171e7a --- /dev/null +++ b/apps/web/scripts/clean-mermaid-format.ts @@ -0,0 +1,46 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +const MDX_DIR = path.join(process.cwd(), 'content/blog'); + +function cleanMermaidFormatting(content: string): string { + // Fix: The repair script reformatted tags badly with extra blank lines + // Pattern: + // Should be: + + // Remove empty lines between '); + + // Fix: Remove trailing whitespace-only lines before id= or title= or showShare= + result = result.replace(/`\}\s*\n\s*\n\s*(id=)/g, '`}\n $1'); + result = result.replace(/`\}\s*\n\s*\n\s*(title=)/g, '`}\n $1'); + result = result.replace(/`\}\s*\n\s*\n\s*(showShare=)/g, '`}\n $1'); + + return result; +} + +function processFiles() { + const files = fs.readdirSync(MDX_DIR).filter(f => f.endsWith('.mdx')); + let fixCount = 0; + + for (const file of files) { + const filePath = path.join(MDX_DIR, file); + const content = fs.readFileSync(filePath, 'utf8'); + const cleaned = cleanMermaidFormatting(content); + + if (content !== cleaned) { + fs.writeFileSync(filePath, cleaned); + fixCount++; + console.log(`✅ Cleaned formatting in ${file}`); + } else { + console.log(`- ${file} OK`); + } + } + + console.log(`\nTotal cleaned: ${fixCount}`); +} + +processFiles(); diff --git a/apps/web/scripts/convert-diagrams-to-children.ts b/apps/web/scripts/convert-diagrams-to-children.ts new file mode 100644 index 0000000..230a25d --- /dev/null +++ b/apps/web/scripts/convert-diagrams-to-children.ts @@ -0,0 +1,365 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +const MDX_DIR = path.join(process.cwd(), 'content/blog'); + +/** + * Convert to {`...`} + * This fixes the RSC serialization issue where template literal props are stripped. + */ +function convertMermaidToChildren(content: string): string { + // Match (self-closing) + // We need a multi-pass approach since the graph prop can appear anywhere in the tag + + const mermaidBlockRegex = //g; + + return content.replace(mermaidBlockRegex, (match) => { + // Extract the graph prop value (template literal) + const graphMatch = match.match(/graph=\{`([\s\S]*?)`\}/); + if (!graphMatch) return match; // No graph prop, skip + + const graphContent = graphMatch[1]; + + // Remove the graph prop from the tag + let cleanedTag = match.replace(/\s*graph=\{`[\s\S]*?`\}\s*/g, ' '); + + // Remove the self-closing /> and add children + cleanedTag = cleanedTag.replace(/\s*\/>$/, ''); + + // Clean up extra whitespace + cleanedTag = cleanedTag.replace(/\s+/g, ' ').trim(); + + return `${cleanedTag}>\n{\`${graphContent}\`}\n`; + }); +} + +/** + * Convert to {`pie\n "label": value\n...`} + */ +function convertDiagramPie(content: string): string { + const pieRegex = //g; + + return content.replace(pieRegex, (match) => { + // Extract data array + const dataMatch = match.match(/data=\{\[([\s\S]*?)\]\}/); + if (!dataMatch) return match; + + const dataStr = dataMatch[1]; + + // Parse the array items: { label: "...", value: ... } + const items: { label: string; value: number }[] = []; + const itemRegex = /\{\s*label:\s*"([^"]+)"\s*,\s*value:\s*(\d+)\s*\}/g; + let itemMatch; + while ((itemMatch = itemRegex.exec(dataStr)) !== null) { + items.push({ label: itemMatch[1], value: parseInt(itemMatch[2]) }); + } + + if (items.length === 0) return match; + + // Build pie chart Mermaid syntax + const pieLines = items.map(item => ` "${item.label}" : ${item.value}`).join('\n'); + const pieGraph = `pie\n${pieLines}`; + + // Extract other props + const titleMatch = match.match(/title="([^"]+)"/); + const captionMatch = match.match(/caption="([^"]+)"/); + const idMatch = match.match(/id="([^"]+)"/); + const showShareMatch = match.match(/showShare=\{(true|false)\}/); + + const title = titleMatch ? titleMatch[1] : ''; + const caption = captionMatch ? captionMatch[1] : ''; + const id = idMatch ? idMatch[1] : ''; + const showShare = showShareMatch ? showShareMatch[1] : 'true'; + + // Build replacement with Mermaid component wrapped in div + let result = `
\n `; + result += `\n{\`${pieGraph}\`}\n`; + if (caption) { + result += `\n
\n ${caption}\n
`; + } + result += `\n
`; + + return result; + }); +} + +/** + * Convert to {`gantt\n...`} + */ +function convertDiagramGantt(content: string): string { + const ganttRegex = //g; + + return content.replace(ganttRegex, (match) => { + // Extract tasks array + const tasksMatch = match.match(/tasks=\{\[([\s\S]*?)\]\}/); + if (!tasksMatch) return match; + + const tasksStr = tasksMatch[1]; + + // Parse the task items + const taskRegex = /\{\s*id:\s*"([^"]+)"\s*,\s*name:\s*"([^"]+)"\s*,\s*start:\s*"([^"]+)"\s*,\s*duration:\s*"([^"]+)"\s*(?:,\s*dependencies:\s*\[([^\]]*)\])?\s*\}/g; + const tasks: { id: string; name: string; start: string; duration: string; deps?: string }[] = []; + let taskMatch; + while ((taskMatch = taskRegex.exec(tasksStr)) !== null) { + tasks.push({ + id: taskMatch[1], + name: taskMatch[2], + start: taskMatch[3], + duration: taskMatch[4], + deps: taskMatch[5] || '', + }); + } + + if (tasks.length === 0) return match; + + // Build gantt chart Mermaid syntax + const ganttLines = tasks.map(task => { + const deps = task.deps ? `, after ${task.deps.replace(/"/g, '').trim()}` : ''; + return ` ${task.name} :${task.id}, ${task.start}, ${task.duration}${deps}`; + }).join('\n'); + const ganttGraph = `gantt\n dateFormat YYYY-MM-DD\n${ganttLines}`; + + // Extract other props + const titleMatch = match.match(/title="([^"]+)"/); + const captionMatch = match.match(/caption="([^"]+)"/); + const idMatch = match.match(/id="([^"]+)"/); + const showShareMatch = match.match(/showShare=\{(true|false)\}/); + + const title = titleMatch ? titleMatch[1] : ''; + const caption = captionMatch ? captionMatch[1] : ''; + const id = idMatch ? idMatch[1] : ''; + const showShare = showShareMatch ? showShareMatch[1] : 'true'; + + let result = `
\n `; + result += `\n{\`${ganttGraph}\`}\n`; + if (caption) { + result += `\n
\n ${caption}\n
`; + } + result += `\n
`; + + return result; + }); +} + +/** + * Convert to {`sequenceDiagram\n...`} + */ +function convertDiagramSequence(content: string): string { + const seqRegex = //g; + + return content.replace(seqRegex, (match) => { + // Extract participants array + const participantsMatch = match.match(/participants=\{\[([\s\S]*?)\]\}/); + if (!participantsMatch) return match; + + // Extract messages array + const messagesMatch = match.match(/messages=\{\[([\s\S]*?)\]\}/); + if (!messagesMatch) return match; + + const participantsStr = participantsMatch[1]; + const messagesStr = messagesMatch[1]; + + // Parse participants + const participants = participantsStr.match(/"([^"]+)"/g)?.map(s => s.replace(/"/g, '')) || []; + + // Parse messages + const msgRegex = /\{\s*from:\s*"([^"]+)"\s*,\s*to:\s*"([^"]+)"\s*,\s*message:\s*"([^"]+)"(?:\s*,\s*type:\s*"([^"]+)")?\s*\}/g; + const messages: { from: string; to: string; message: string; type?: string }[] = []; + let msgMatch; + while ((msgMatch = msgRegex.exec(messagesStr)) !== null) { + messages.push({ + from: msgMatch[1], + to: msgMatch[2], + message: msgMatch[3], + type: msgMatch[4], + }); + } + + if (participants.length === 0 || messages.length === 0) return match; + + // Build sequence diagram Mermaid syntax + const getArrow = (type?: string) => { + switch (type) { + case 'dotted': return '-->>'; + case 'async': return '->>'; + default: return '->>'; + } + }; + + const participantLines = participants.map(p => ` participant ${p}`).join('\n'); + const messageLines = messages.map(m => ` ${m.from}${getArrow(m.type)}${m.to}: ${m.message}`).join('\n'); + const seqGraph = `sequenceDiagram\n${participantLines}\n${messageLines}`; + + // Extract other props + const titleMatch = match.match(/title="([^"]+)"/); + const captionMatch = match.match(/caption="([^"]+)"/); + const idMatch = match.match(/id="([^"]+)"/); + const showShareMatch = match.match(/showShare=\{(true|false)\}/); + + const title = titleMatch ? titleMatch[1] : ''; + const caption = captionMatch ? captionMatch[1] : ''; + const id = idMatch ? idMatch[1] : ''; + const showShare = showShareMatch ? showShareMatch[1] : 'true'; + + let result = `
\n `; + result += `\n{\`${seqGraph}\`}\n`; + if (caption) { + result += `\n
\n ${caption}\n
`; + } + result += `\n
`; + + return result; + }); +} + +/** + * Convert to {`timeline\n...`} + */ +function convertDiagramTimeline(content: string): string { + const timelineRegex = //g; + + return content.replace(timelineRegex, (match) => { + const eventsMatch = match.match(/events=\{\[([\s\S]*?)\]\}/); + if (!eventsMatch) return match; + + const eventsStr = eventsMatch[1]; + + const eventRegex = /\{\s*year:\s*"([^"]+)"\s*,\s*title:\s*"([^"]+)"\s*\}/g; + const events: { year: string; title: string }[] = []; + let eventMatch; + while ((eventMatch = eventRegex.exec(eventsStr)) !== null) { + events.push({ year: eventMatch[1], title: eventMatch[2] }); + } + + if (events.length === 0) return match; + + const titleMatch = match.match(/title="([^"]+)"/); + const captionMatch = match.match(/caption="([^"]+)"/); + const idMatch = match.match(/id="([^"]+)"/); + const showShareMatch = match.match(/showShare=\{(true|false)\}/); + + const title = titleMatch ? titleMatch[1] : ''; + const caption = captionMatch ? captionMatch[1] : ''; + const id = idMatch ? idMatch[1] : ''; + const showShare = showShareMatch ? showShareMatch[1] : 'true'; + + const eventLines = events.map(e => ` ${e.year} : ${e.title}`).join('\n'); + const timelineGraph = `timeline\n title ${title || 'Timeline'}\n${eventLines}`; + + let result = `
\n `; + result += `\n{\`${timelineGraph}\`}\n`; + if (caption) { + result += `\n
\n ${caption}\n
`; + } + result += `\n
`; + + return result; + }); +} + +/** + * Convert to {`stateDiagram-v2\n...`} + */ +function convertDiagramState(content: string): string { + const stateRegex = //g; + + return content.replace(stateRegex, (match) => { + // Extract transitions + const transitionsMatch = match.match(/transitions=\{\[([\s\S]*?)\]\}/); + if (!transitionsMatch) return match; + + const transitionsStr = transitionsMatch[1]; + + const transRegex = /\{\s*from:\s*"([^"]+)"\s*,\s*to:\s*"([^"]+)"(?:\s*,\s*label:\s*"([^"]+)")?\s*\}/g; + const transitions: { from: string; to: string; label?: string }[] = []; + let transMatch; + while ((transMatch = transRegex.exec(transitionsStr)) !== null) { + transitions.push({ + from: transMatch[1], + to: transMatch[2], + label: transMatch[3], + }); + } + + if (transitions.length === 0) return match; + + // Extract initialState + const initialStateMatch = match.match(/initialState="([^"]+)"/); + const initialState = initialStateMatch ? initialStateMatch[1] : ''; + + // Extract finalStates + const finalStatesMatch = match.match(/finalStates=\{\[([^\]]*)\]\}/); + const finalStatesStr = finalStatesMatch ? finalStatesMatch[1] : ''; + const finalStates = finalStatesStr.match(/"([^"]+)"/g)?.map(s => s.replace(/"/g, '')) || []; + + const titleMatch = match.match(/title="([^"]+)"/); + const captionMatch = match.match(/caption="([^"]+)"/); + const idMatch = match.match(/id="([^"]+)"/); + const showShareMatch = match.match(/showShare=\{(true|false)\}/); + + const title = titleMatch ? titleMatch[1] : ''; + const caption = captionMatch ? captionMatch[1] : ''; + const id = idMatch ? idMatch[1] : ''; + const showShare = showShareMatch ? showShareMatch[1] : 'true'; + + let stateLines = 'stateDiagram-v2'; + if (initialState) { + stateLines += `\n [*] --> ${initialState}`; + } + stateLines += '\n' + transitions.map(t => { + const label = t.label ? ` : ${t.label}` : ''; + return ` ${t.from} --> ${t.to}${label}`; + }).join('\n'); + stateLines += '\n' + finalStates.map(s => ` ${s} --> [*]`).join('\n'); + + let result = `
\n `; + result += `\n{\`${stateLines}\`}\n`; + if (caption) { + result += `\n
\n ${caption}\n
`; + } + result += `\n
`; + + return result; + }); +} + +function processFiles() { + const files = fs.readdirSync(MDX_DIR).filter(f => f.endsWith('.mdx')); + + for (const file of files) { + const filePath = path.join(MDX_DIR, file); + let content = fs.readFileSync(filePath, 'utf8'); + const original = content; + + content = convertMermaidToChildren(content); + content = convertDiagramPie(content); + content = convertDiagramGantt(content); + content = convertDiagramSequence(content); + content = convertDiagramTimeline(content); + content = convertDiagramState(content); + + if (content !== original) { + fs.writeFileSync(filePath, content); + console.log(`✅ Converted diagrams in ${file}`); + } else { + console.log(`- ${file} (no changes needed)`); + } + } +} + +processFiles(); diff --git a/apps/web/scripts/convert-to-children-clean.ts b/apps/web/scripts/convert-to-children-clean.ts new file mode 100644 index 0000000..9c810b8 --- /dev/null +++ b/apps/web/scripts/convert-to-children-clean.ts @@ -0,0 +1,56 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +const MDX_DIR = path.join(process.cwd(), 'content/blog'); + +/** + * Convert ugly single-line graph="..." props to clean multi-line children. + * + * FROM: + * + * + * TO: + * + * graph TD + * A-->B + * B-->C + * + */ +function convertToChildren(content: string): string { + // Match + const mermaidRegex = /]*?)\/>/g; + + return content.replace(mermaidRegex, (match, graphValue, otherProps) => { + // Unescape \n to real newlines + const cleanGraph = graphValue.replace(/\\n/g, '\n'); + + // Clean up other props + const cleanProps = otherProps.trim(); + + // Build the new format with children + return `\n${cleanGraph}\n`; + }); +} + +function processFiles() { + const files = fs.readdirSync(MDX_DIR).filter(f => f.endsWith('.mdx')); + let fixCount = 0; + + for (const file of files) { + const filePath = path.join(MDX_DIR, file); + const content = fs.readFileSync(filePath, 'utf8'); + const fixed = convertToChildren(content); + + if (content !== fixed) { + fs.writeFileSync(filePath, fixed); + fixCount++; + console.log(`✅ Converted to children: ${file}`); + } else { + console.log(`- ${file} (no changes needed)`); + } + } + + console.log(`\nTotal converted: ${fixCount}`); +} + +processFiles(); diff --git a/apps/web/scripts/convert-to-plain-prop.ts b/apps/web/scripts/convert-to-plain-prop.ts new file mode 100644 index 0000000..2fe4c35 --- /dev/null +++ b/apps/web/scripts/convert-to-plain-prop.ts @@ -0,0 +1,46 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +const MDX_DIR = path.join(process.cwd(), 'content/blog'); + +/** + * Convert graph={"..."} to graph="..." (plain string prop without JSX expression wrapper). + * MDXRemote RSC strips JSX expression props but keeps plain string props. + * + * But we also need the escape sequences to go through. + * The plain string prop `graph="graph TD\nA-->B"` will have the \n treated as + * literal characters by MDX's parser, not as a newline. The Mermaid component + * then unescapes them. + */ +function convertToPlainStringProps(content: string): string { + // Match graph={" ... "} and convert to graph="..." + // The content inside should already have escaped newlines and quotes + const pattern = /graph=\{"((?:[^"\\]|\\.)*)"\}/g; + + return content.replace(pattern, (match, graphContent) => { + return `graph="${graphContent}"`; + }); +} + +function processFiles() { + const files = fs.readdirSync(MDX_DIR).filter(f => f.endsWith('.mdx')); + let fixCount = 0; + + for (const file of files) { + const filePath = path.join(MDX_DIR, file); + const content = fs.readFileSync(filePath, 'utf8'); + const fixed = convertToPlainStringProps(content); + + if (content !== fixed) { + fs.writeFileSync(filePath, fixed); + fixCount++; + console.log(`✅ Converted: ${file}`); + } else { + console.log(`- ${file} (no changes needed)`); + } + } + + console.log(`\nTotal converted: ${fixCount}`); +} + +processFiles(); diff --git a/apps/web/scripts/convert-to-plain-string.ts b/apps/web/scripts/convert-to-plain-string.ts new file mode 100644 index 0000000..650f901 --- /dev/null +++ b/apps/web/scripts/convert-to-plain-string.ts @@ -0,0 +1,60 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +const MDX_DIR = path.join(process.cwd(), 'content/blog'); + +/** + * Convert all Mermaid children syntax back to graph prop, + * BUT use a regular double-quoted string with escaped newlines instead of template literals. + * + * MDXRemote RSC strips template literals! + * + * Convert: + * + * {`graph TD + * A --> B`} + * + * + * To: + * B"} id="..." title="..." showShare={true} /> + */ +function convertToPlainStringProp(content: string): string { + // Match {\`...\`} + const mermaidChildrenRegex = /]*?)>\s*\{`([\s\S]*?)`\}\s*<\/Mermaid>/g; + + return content.replace(mermaidChildrenRegex, (match, propsStr, graphContent) => { + // Escape double quotes in the graph content + const escapedGraph = graphContent + .replace(/\\/g, '\\\\') // escape backslashes first + .replace(/"/g, '\\"') // escape double quotes + .replace(/\n/g, '\\n'); // escape newlines + + // Clean up props string + const cleanProps = propsStr.trim(); + + return ``; + }); +} + +function processFiles() { + const files = fs.readdirSync(MDX_DIR).filter(f => f.endsWith('.mdx')); + let fixCount = 0; + + for (const file of files) { + const filePath = path.join(MDX_DIR, file); + const content = fs.readFileSync(filePath, 'utf8'); + const fixed = convertToPlainStringProp(content); + + if (content !== fixed) { + fs.writeFileSync(filePath, fixed); + fixCount++; + console.log(`✅ Converted to plain string: ${file}`); + } else { + console.log(`- ${file} (no Mermaid children found)`); + } + } + + console.log(`\nTotal converted: ${fixCount}`); +} + +processFiles(); diff --git a/apps/web/scripts/final-diagram-fix.ts b/apps/web/scripts/final-diagram-fix.ts new file mode 100644 index 0000000..73050ed --- /dev/null +++ b/apps/web/scripts/final-diagram-fix.ts @@ -0,0 +1,47 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +const MDX_DIR = path.join(process.cwd(), 'content/blog'); + +/** + * FINAL ATTEMPT: Standardize EVERYTHING in Mermaid blocks to double quotes. + * + * 1. Find all text inside .... + * 2. Replace any ['Label'] or ['Label's'] or ["Label"] patterns. + * 3. Enforce ["Label"] for all labels. + * 4. Remove any internal single quotes that break parsing. + */ +function finalMermaidFix(content: string): string { + const mermaidRegex = /(]*>)([\s\S]*?)(<\/Mermaid>)/g; + + return content.replace(mermaidRegex, (match, open, body, close) => { + let fixedBody = body; + + // Convert common label syntax to clean double quotes + // Match: [followed by optional space and any quote, capture content, end with optional quote and space] + fixedBody = fixedBody.replace(/\[\s*['"]?([^\]'"]+?)['"]?\s*\]/g, (m, label) => { + // Clean the label: remove any internal quotes that could cause issues + const cleanLabel = label.replace(/['"]/g, "").trim(); + return `["${cleanLabel}"]`; + }); + + // Also handle Pie charts which use 'Label' : value + fixedBody = fixedBody.replace(/^\s*'([^']+)'\s*:/gm, (m, label) => { + const cleanLabel = label.replace(/['"]/g, "").trim(); + return ` "${cleanLabel}" :`; + }); + + return open + fixedBody + close; + }); +} + +const files = fs.readdirSync(MDX_DIR).filter(f => f.endsWith('.mdx')); +for (const file of files) { + const fp = path.join(MDX_DIR, file); + const content = fs.readFileSync(fp, 'utf8'); + const fixed = finalMermaidFix(content); + if (content !== fixed) { + fs.writeFileSync(fp, fixed); + console.log(`✅ Fixed potentially problematic syntax in: ${file}`); + } +} diff --git a/apps/web/scripts/fix-graph-quotes.ts b/apps/web/scripts/fix-graph-quotes.ts new file mode 100644 index 0000000..b9d974d --- /dev/null +++ b/apps/web/scripts/fix-graph-quotes.ts @@ -0,0 +1,86 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +const MDX_DIR = path.join(process.cwd(), 'content/blog'); + +/** + * Fix escaped double quotes in Mermaid graph props. + * + * The graph prop contains \" which is invalid in MDX attribute syntax. + * Replace \" with ' (single quote) - Mermaid supports both. + * + * Also fix \\n to just \n (single backslash) - the MDX parser + * will treat \n as literal characters, and the Mermaid component + * will unescape them. + */ +function fixGraphQuotes(content: string): string { + // Match graph="..." prop (the entire value including escaped content) + // This is tricky because the value can contain escaped quotes + // We need to match from graph=" to the closing " that ends the prop value + + // Strategy: Find graph=" then scan forward, tracking escape sequences + const graphPropStart = 'graph="'; + let result = ''; + let i = 0; + + while (i < content.length) { + const idx = content.indexOf(graphPropStart, i); + if (idx === -1) { + result += content.slice(i); + break; + } + + // Copy everything up to and including graph=" + result += content.slice(i, idx + graphPropStart.length); + + // Now scan the value, replacing \" with ' + let j = idx + graphPropStart.length; + let graphValue = ''; + + while (j < content.length) { + if (content[j] === '\\' && content[j + 1] === '"') { + // Replace \" with ' + graphValue += "'"; + j += 2; + } else if (content[j] === '\\' && content[j + 1] === '\\') { + // Keep \\ as is + graphValue += '\\\\'; + j += 2; + } else if (content[j] === '"') { + // End of attribute value + break; + } else { + graphValue += content[j]; + j++; + } + } + + result += graphValue; + i = j; // Continue from the closing quote (will be added in next iteration) + } + + return result; +} + +function processFiles() { + const files = fs.readdirSync(MDX_DIR).filter(f => f.endsWith('.mdx')); + let fixCount = 0; + + for (const file of files) { + const filePath = path.join(MDX_DIR, file); + const content = fs.readFileSync(filePath, 'utf8'); + const fixed = fixGraphQuotes(content); + + if (content !== fixed) { + fs.writeFileSync(filePath, fixed); + fixCount++; + console.log(`✅ Fixed quotes: ${file}`); + } else { + console.log(`- ${file} (no changes needed)`); + } + } + + console.log(`\nTotal fixed: ${fixCount}`); +} + +processFiles(); diff --git a/apps/web/scripts/fix-mdx-whitespace.ts b/apps/web/scripts/fix-mdx-whitespace.ts new file mode 100644 index 0000000..e13e5cd --- /dev/null +++ b/apps/web/scripts/fix-mdx-whitespace.ts @@ -0,0 +1,123 @@ +#!/usr/bin/env tsx + +/** + * Fix missing whitespace in MDX files by comparing with TSX originals + */ + +import * as fs from 'fs'; +import * as path from 'path'; + +// Mapping of TSX component names to MDX slugs +const TSX_TO_MDX_MAP: Record = { + 'PageSpeedFails': 'why-pagespeed-fails', + 'SlowLoadingDebt': 'slow-loading-costs-customers', + 'AgencySlowdown': 'why-agencies-are-slow', + 'WordPressPlugins': 'hidden-costs-of-wordpress-plugins', + 'WebsiteStability': 'why-websites-break-after-updates', + 'CookieFreeDesign': 'website-without-cookie-banners', + 'LocalCloud': 'no-us-cloud-platforms', + 'GDPRSystem': 'gdpr-conformity-system-approach', + 'VendorLockIn': 'builder-systems-threaten-independence', + 'PrivacyAnalytics': 'analytics-without-tracking', + 'GreenIT': 'green-it-sustainable-web', + 'FixedPrice': 'fixed-price-digital-projects', + 'BuildFirst': 'build-first-digital-architecture', + 'MaintenanceNoCMS': 'maintenance-for-headless-systems', + 'Longevity': 'digital-longevity-architecture', + 'CleanCode': 'clean-code-for-business-value', + 'ResponsiveDesign': 'responsive-design-high-fidelity', + 'HostingOps': 'professional-hosting-operations', + 'NoTemplates': 'why-no-templates-matter', + 'CRMSync': 'crm-synchronization-headless', +}; + +const TSX_BASE = path.join(process.cwd(), 'src/components/blog/posts'); +const MDX_BASE = path.join(process.cwd(), 'content/blog'); + +function findTsxFile(componentName: string): string | null { + for (const group of ['Group1', 'Group2', 'Group3', 'Group4']) { + const tsxPath = path.join(TSX_BASE, group, `${componentName}.tsx`); + if (fs.existsSync(tsxPath)) { + return tsxPath; + } + } + return null; +} + +function fixWhitespace() { + let totalFixed = 0; + + for (const [tsxName, mdxSlug] of Object.entries(TSX_TO_MDX_MAP)) { + const tsxPath = findTsxFile(tsxName); + const mdxPath = path.join(MDX_BASE, `${mdxSlug}.mdx`); + + if (!tsxPath || !fs.existsSync(mdxPath)) { + console.log(`⚠️ Skipping ${tsxName}: files not found`); + continue; + } + + const tsxContent = fs.readFileSync(tsxPath, 'utf-8'); + let mdxContent = fs.readFileSync(mdxPath, 'utf-8'); + + // Count occurrences of {" "} in both files + const tsxSpaces = (tsxContent.match(/\{" "\}/g) || []).length; + const mdxSpacesBefore = (mdxContent.match(/\{" "\}/g) || []).length; + + if (tsxSpaces === 0) { + console.log(`✓ ${mdxSlug}: No whitespace needed`); + continue; + } + + // Extract all lines with {" "} from TSX + const tsxLines = tsxContent.split('\n'); + const spacedLines: Array<{ lineNum: number; content: string }> = []; + + tsxLines.forEach((line, idx) => { + if (line.includes('{" "}')) { + spacedLines.push({ lineNum: idx, content: line.trim() }); + } + }); + + // For each spaced line in TSX, find similar content in MDX and add {" "} + let fixCount = 0; + for (const { content } of spacedLines) { + // Extract the text pattern before {" "} + const match = content.match(/(.+?)\{" "\}/); + if (!match) continue; + + const textBefore = match[1].trim(); + + // Find this pattern in MDX without the space + const searchPattern = new RegExp( + textBefore.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '(?!\\{" "\\})', + 'g' + ); + + const newMdxContent = mdxContent.replace(searchPattern, (matched) => { + // Only add {" "} if it's not already there and if it's followed by a tag + if (mdxContent.indexOf(matched + '{" "}') === -1 && + mdxContent.indexOf(matched) < mdxContent.indexOf('<', mdxContent.indexOf(matched))) { + fixCount++; + return matched + '{" "}'; + } + return matched; + }); + + mdxContent = newMdxContent; + } + + const mdxSpacesAfter = (mdxContent.match(/\{" "\}/g) || []).length; + + if (fixCount > 0) { + fs.writeFileSync(mdxPath, mdxContent, 'utf-8'); + console.log(`✓ ${mdxSlug}: Fixed ${fixCount} whitespace issues (${mdxSpacesBefore} → ${mdxSpacesAfter})`); + totalFixed += fixCount; + } else { + console.log(`✓ ${mdxSlug}: Already correct (${mdxSpacesBefore} spaces)`); + } + } + + console.log(`\n✅ Total whitespace fixes: ${totalFixed}`); +} + +fixWhitespace(); diff --git a/apps/web/scripts/fix-mermaid-apostrophes.ts b/apps/web/scripts/fix-mermaid-apostrophes.ts new file mode 100644 index 0000000..8509b45 --- /dev/null +++ b/apps/web/scripts/fix-mermaid-apostrophes.ts @@ -0,0 +1,61 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +const MDX_DIR = path.join(process.cwd(), 'content/blog'); + +/** + * Fix apostrophes in Mermaid labels by removing them. + * + * Mermaid parser treats ' as a quote delimiter even inside ["..."]. + * Replace ' with nothing or use HTML entity ' (but simpler to just remove). + */ +function fixMermaidApostrophes(content: string): string { + // Find all Mermaid blocks + const mermaidBlockRegex = /(]*>)([\s\S]*?)(<\/Mermaid>)/g; + + return content.replace(mermaidBlockRegex, (match, openTag, mermaidContent, closeTag) => { + // Within Mermaid content, find all ["..."] labels and remove apostrophes + const fixed = mermaidContent.replace(/\["([^"]*)"\]/g, (m: string, label: string) => { + // Remove all apostrophes from the label + const cleanLabel = label.replace(/'/g, ''); + return `["${cleanLabel}"]`; + }); + + return openTag + fixed + closeTag; + }); +} + +function processFiles() { + const files = fs.readdirSync(MDX_DIR).filter(f => f.endsWith('.mdx')); + let fixCount = 0; + let totalApostrophes = 0; + + for (const file of files) { + const filePath = path.join(MDX_DIR, file); + const content = fs.readFileSync(filePath, 'utf8'); + + // Count apostrophes in Mermaid blocks before fixing + const mermaidBlocks = content.match(/]*>[\s\S]*?<\/Mermaid>/g) || []; + for (const block of mermaidBlocks) { + const apostrophes = (block.match(/\["[^"]*'[^"]*"\]/g) || []).length; + if (apostrophes > 0) { + totalApostrophes += apostrophes; + } + } + + const fixed = fixMermaidApostrophes(content); + + if (content !== fixed) { + fs.writeFileSync(filePath, fixed); + fixCount++; + console.log(`✅ Fixed apostrophes: ${file}`); + } else { + console.log(`- ${file} (no apostrophes found)`); + } + } + + console.log(`\nTotal files fixed: ${fixCount}`); + console.log(`Total apostrophes removed: ${totalApostrophes}`); +} + +processFiles(); diff --git a/apps/web/scripts/fix-mermaid-labels-strict.ts b/apps/web/scripts/fix-mermaid-labels-strict.ts new file mode 100644 index 0000000..5d167d0 --- /dev/null +++ b/apps/web/scripts/fix-mermaid-labels-strict.ts @@ -0,0 +1,69 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +const MDX_DIR = path.join(process.cwd(), 'content/blog'); + +/** + * STRICTLY fixes Mermaid node labels. + * + * Goal: + * 1. Find all content inside [...] in Mermaid blocks. + * 2. Strip ALL outer quotes (single or double). + * 3. Sanitize the inner text: + * - Remove/Replace internal double quotes + * - Remove/Replace internal single quotes (to avoid any ambiguity) + * 4. Wrap strictly in ["..."]. + */ +function fixMermaidLabels(content: string): string { + // Find Mermaid blocks + return content.replace(/(]*>)([\s\S]*?)(<\/Mermaid>)/g, (match, open, body, close) => { + + // Process the body line by line to be safe, or just regex the labels. + // Regex for labels: \[ followed by anything until \] + // Note: We assume labels don't contain nested brackets for now (Mermaid usually doesn't). + const fixedBody = body.replace(/\[([^\]]+)\]/g, (labelMatch, innerContent) => { + let text = innerContent.trim(); + + // Check if it looks like a quoted label + const hasOuterQuotes = /^['"]|['"]$/.test(text); + if (hasOuterQuotes) { + // Remove ALL starting/ending quotes (handling multiple if messed up) + text = text.replace(/^['"]+|['"]+$/g, ''); + } + + // Sanitize internal text + // Replace " with ' to avoid breaking the outer double quotes + text = text.replace(/"/g, "'"); + + // Verify parsing safety: + // Replace ' with space or nothing if we want to be super safe, + // but "Text with 'quotes'" SHOULD be valid in Mermaid. + // However, the previous error might have been due to MDX interference. + // Let's keep single quotes inside, but ensure outer are double. + + // WAIT: The specific error was `B['Server ... 'inner' ...']`. + // If we convert to `B["Server ... 'inner' ..."]`, it should work. + + return `["${text}"]`; + }); + + return open + fixedBody + close; + }); +} + +const files = fs.readdirSync(MDX_DIR).filter(f => f.endsWith('.mdx')); +let fixedCount = 0; + +for (const file of files) { + const filePath = path.join(MDX_DIR, file); + const content = fs.readFileSync(filePath, 'utf8'); + const fixed = fixMermaidLabels(content); + + if (content !== fixed) { + fs.writeFileSync(filePath, fixed); + console.log(`✅ Fixed labels in: ${file}`); + fixedCount++; + } +} + +console.log(`\nFixed ${fixedCount} files.`); diff --git a/apps/web/scripts/fix-mermaid-quotes.ts b/apps/web/scripts/fix-mermaid-quotes.ts new file mode 100644 index 0000000..e206594 --- /dev/null +++ b/apps/web/scripts/fix-mermaid-quotes.ts @@ -0,0 +1,49 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +const MDX_DIR = path.join(process.cwd(), 'content/blog'); + +/** + * Fix ALL quote variations in Mermaid labels to use consistent double quotes. + * + * Handles: + * - ['Label'] → ["Label"] + * - ["Label'] → ["Label"] + * - ['Label"] → ["Label"] + * - ["Label"] → ["Label"] (already correct) + */ +function fixMermaidQuotes(content: string): string { + // Find all Mermaid blocks (between and ) + const mermaidBlockRegex = /(]*>)([\s\S]*?)(<\/Mermaid>)/g; + + return content.replace(mermaidBlockRegex, (match, openTag, mermaidContent, closeTag) => { + // Replace all variations: [' or [" at start, '] or "] at end + // Match pattern: [ followed by ' or ", then content, then ' or ", then ] + const fixed = mermaidContent.replace(/\[['"]([^'"]*)['"]\]/g, '["$1"]'); + + return openTag + fixed + closeTag; + }); +} + +function processFiles() { + const files = fs.readdirSync(MDX_DIR).filter(f => f.endsWith('.mdx')); + let fixCount = 0; + + for (const file of files) { + const filePath = path.join(MDX_DIR, file); + const content = fs.readFileSync(filePath, 'utf8'); + const fixed = fixMermaidQuotes(content); + + if (content !== fixed) { + fs.writeFileSync(filePath, fixed); + fixCount++; + console.log(`✅ Fixed Mermaid quotes: ${file}`); + } else { + console.log(`- ${file} (no changes needed)`); + } + } + + console.log(`\nTotal fixed: ${fixCount}`); +} + +processFiles(); diff --git a/apps/web/scripts/repair-mermaid.ts b/apps/web/scripts/repair-mermaid.ts new file mode 100644 index 0000000..10e7369 --- /dev/null +++ b/apps/web/scripts/repair-mermaid.ts @@ -0,0 +1,53 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +const MDX_DIR = path.join(process.cwd(), 'content/blog'); + +function repairMermaidSyntax(content: string): string { + // 1. Convert to ... style or just fix the graph prop + // Actually, let's keep the graph prop but make sure the content is VERY safe. + + const mermaidRegex = //g; + + return content.replace(mermaidRegex, (match, before, graphLiteral, after) => { + let graphContent = graphLiteral.slice(1, -1); + + // Replace all {Label} with ["Label"] + graphContent = graphContent.replace(/\{([^{}]+)\}/g, '["$1"]'); + + // Sometimes people use double {{Label}} + graphContent = graphContent.replace(/\{\{([^{}]+)\}\}/g, '["$1"]'); + + // Remove any trailing backticks inside that might have been accidentally added + graphContent = graphContent.trim(); + + return ``; + }); +} + +// Additional fix for other diagram components that might have similar issues with props +function repairOtherDiagrams(content: string): string { + // For DiagramSequence, DiagramTimeline etc., we often pass arrays of objects. + // MDX handles these better, but let's make sure there are no weird backticks. + return content; +} + +function processFiles() { + const files = fs.readdirSync(MDX_DIR).filter(f => f.endsWith('.mdx')); + + for (const file of files) { + const filePath = path.join(MDX_DIR, file); + const content = fs.readFileSync(filePath, 'utf8'); + let repaired = repairMermaidSyntax(content); + repaired = repairOtherDiagrams(repaired); + + if (content !== repaired) { + fs.writeFileSync(filePath, repaired); + console.log(`✅ Repaired Mermaid syntax in ${file}`); + } else { + console.log(`- Checked ${file}`); + } + } +} + +processFiles(); diff --git a/apps/web/src/components/ArticleBlockquote.tsx b/apps/web/src/components/ArticleBlockquote.tsx index 3eba190..9889f22 100644 --- a/apps/web/src/components/ArticleBlockquote.tsx +++ b/apps/web/src/components/ArticleBlockquote.tsx @@ -19,19 +19,19 @@ interface BlockquoteProps { className?: string; } -export const Blockquote: React.FC = ({ children, className = '' }) => ( -
+export const ArticleBlockquote: React.FC = ({ children, className = '' }) => ( +
{children}
); interface CodeBlockProps { - code?: string; - children?: React.ReactNode; - language?: string; - showLineNumbers?: boolean; - className?: string; - } + code?: string; + children?: React.ReactNode; + language?: string; + showLineNumbers?: boolean; + className?: string; +} // Language mapping for Prism.js @@ -70,54 +70,54 @@ const highlightCode = (code: string, language: string): { html: string; prismLan console.warn('Prism highlighting failed:', error); return { html: code.trim(), prismLanguage: 'text' }; } - }; +}; export const CodeBlock: React.FC = ({ - code, - children, - language = 'text', - showLineNumbers = false, - className = '' - }) => { - const codeContent = code || (typeof children === 'string' ? children : String(children || '')).trim(); - const { html: highlightedCode, prismLanguage } = language !== 'text' ? highlightCode(codeContent, language) : { html: codeContent, prismLanguage: 'text' }; - const lines = codeContent.split('\n'); + code, + children, + language = 'text', + showLineNumbers = false, + className = '' +}) => { + const codeContent = code || (typeof children === 'string' ? children : String(children || '')).trim(); + const { html: highlightedCode, prismLanguage } = language !== 'text' ? highlightCode(codeContent, language) : { html: codeContent, prismLanguage: 'text' }; + const lines = codeContent.split('\n'); - return ( - <> -