fix(blog): optimize component share logic, typography, and modal layouts
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 5s
Build & Deploy / 🏗️ Build (push) Failing after 14s
Build & Deploy / 🧪 QA (push) Failing after 1m48s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 5s
Build & Deploy / 🏗️ Build (push) Failing after 14s
Build & Deploy / 🧪 QA (push) Failing after 1m48s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import mermaid from "mermaid";
|
||||
import { DiagramShareButton } from "./DiagramShareButton";
|
||||
import { ComponentShareButton } from "./ComponentShareButton";
|
||||
import { Reveal } from "./Reveal";
|
||||
|
||||
interface MermaidProps {
|
||||
graph?: string;
|
||||
@@ -104,17 +105,9 @@ const MermaidInternal: React.FC<MermaidProps> = ({
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log(`[Mermaid DEBUG] id=${providedId}, graph prop length=${graph?.length ?? 'undefined'}, rawGraph length=${rawGraph.length}, sanitizedGraph length=${sanitizedGraph.length}`);
|
||||
if (graph?.length === 0) {
|
||||
console.log(`[Mermaid DEBUG] id=${providedId} EMPTY graph prop! children:`, children);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const generatedId = providedId || `mermaid-${Math.random().toString(36).substring(2, 11)}`;
|
||||
setId(generatedId);
|
||||
console.log(`[Mermaid DEBUG] id=${generatedId}, provided=${providedId}, graph length=${graph?.length ?? 'undefined'}`);
|
||||
}, [providedId]);
|
||||
|
||||
// Observer to detect when the component is actually in view and layout is ready
|
||||
@@ -138,18 +131,15 @@ const MermaidInternal: React.FC<MermaidProps> = ({
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(`[Mermaid DEBUG] Main effect triggered. id=${id}, isVisible=${isVisible}, isRendered=${isRendered}`);
|
||||
if (!isVisible || !id || isRendered) {
|
||||
console.log(`[Mermaid DEBUG] Main effect early return (will not render). isVisible=${isVisible}, id=${id}, isRendered=${isRendered}`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`[Mermaid DEBUG] Initializing mermaid for ${id}...`);
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
theme: "base",
|
||||
darkMode: false,
|
||||
htmlLabels: false, // Added this line as per instruction
|
||||
htmlLabels: false,
|
||||
flowchart: {
|
||||
useMaxWidth: true,
|
||||
htmlLabels: false,
|
||||
@@ -174,25 +164,17 @@ const MermaidInternal: React.FC<MermaidProps> = ({
|
||||
primaryBorderColor: "#cbd5e1", // slate-300
|
||||
lineColor: "#64748b", // slate-500
|
||||
secondaryColor: "#f1f5f9", // slate-100
|
||||
tertiaryColor: "#e2e8f0", // slate-200 // Background colors
|
||||
tertiaryColor: "#e2e8f0", // slate-200
|
||||
background: "#ffffff",
|
||||
mainBkg: "#ffffff",
|
||||
secondBkg: "#f8fafc",
|
||||
tertiaryBkg: "#f1f5f9",
|
||||
|
||||
// Text colors
|
||||
textColor: "#1e293b",
|
||||
labelTextColor: "#475569",
|
||||
|
||||
// Node styling
|
||||
nodeBorder: "#cbd5e1",
|
||||
clusterBkg: "#f8fafc",
|
||||
clusterBorder: "#cbd5e1",
|
||||
|
||||
// Edge/line styling
|
||||
edgeLabelBackground: "#ffffff",
|
||||
|
||||
// Font
|
||||
fontFamily: "var(--font-geist-mono), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace",
|
||||
fontSize: fontSize || "18px",
|
||||
nodeFontSize: nodeFontSize || fontSize || "18px",
|
||||
@@ -203,32 +185,25 @@ const MermaidInternal: React.FC<MermaidProps> = ({
|
||||
titleFontSize: titleFontSize || "24px",
|
||||
sectionFontSize: sectionFontSize || fontSize || "18px",
|
||||
legendFontSize: legendFontSize || fontSize || "18px",
|
||||
|
||||
// Pie Chart Colors - High Contrast Industrial Palette
|
||||
pie1: "#0f172a", // Deep Navy
|
||||
pie2: "#334155", // Slate Blue
|
||||
pie3: "#64748b", // Steel Gray
|
||||
pie4: "#94a3b8", // Muted Steel
|
||||
pie5: "#cbd5e1", // Concrete
|
||||
pie6: "#1e293b", // Slate 800
|
||||
pie7: "#475569", // Slate 600
|
||||
pie8: "#000000", // Pure Black for accents
|
||||
pie9: "#e2e8f0", // Light Concrete
|
||||
pie10: "#020617", // Slate 950
|
||||
pie11: "#525252", // Neutral 600
|
||||
pie12: "#262626", // Neutral 800
|
||||
pie1: "#0f172a",
|
||||
pie2: "#334155",
|
||||
pie3: "#64748b",
|
||||
pie4: "#94a3b8",
|
||||
pie5: "#cbd5e1",
|
||||
pie6: "#1e293b",
|
||||
pie7: "#475569",
|
||||
pie8: "#000000",
|
||||
pie9: "#e2e8f0",
|
||||
pie10: "#020617",
|
||||
pie11: "#525252",
|
||||
pie12: "#262626",
|
||||
},
|
||||
securityLevel: "loose",
|
||||
});
|
||||
|
||||
const renderGraph = async () => {
|
||||
if (!wrapperRef.current) return;
|
||||
|
||||
// CRITICAL: Ensure invalid dimensions don't crash d3
|
||||
if (wrapperRef.current.clientWidth === 0) {
|
||||
console.warn("Mermaid: Container width is 0, deferring render", id);
|
||||
return;
|
||||
}
|
||||
if (wrapperRef.current.clientWidth === 0) return;
|
||||
|
||||
const maxRetries = 3;
|
||||
let attempt = 0;
|
||||
@@ -237,19 +212,10 @@ const MermaidInternal: React.FC<MermaidProps> = ({
|
||||
while (attempt < maxRetries && !success) {
|
||||
attempt++;
|
||||
try {
|
||||
if (!sanitizedGraph) {
|
||||
console.warn("Mermaid: Empty graph definition received, skipping render");
|
||||
return;
|
||||
}
|
||||
if (!sanitizedGraph) return;
|
||||
if (!mermaid.render) return;
|
||||
|
||||
if (!mermaid.render) {
|
||||
console.warn("Mermaid not ready");
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate a unique ID for the SVG to prevent collisions during retries
|
||||
const uniqueSvgId = `${id}-svg-${Date.now()}`;
|
||||
// Render into a detached container to avoid React DOM conflicts
|
||||
const tempDiv = document.createElement('div');
|
||||
document.body.appendChild(tempDiv);
|
||||
tempDiv.style.position = 'absolute';
|
||||
@@ -258,10 +224,8 @@ const MermaidInternal: React.FC<MermaidProps> = ({
|
||||
|
||||
let rawSvg: string;
|
||||
try {
|
||||
console.log(`[Mermaid DEBUG] Calling mermaid.render for ${id}...`);
|
||||
const result = await mermaid.render(uniqueSvgId, sanitizedGraph, tempDiv);
|
||||
rawSvg = result.svg;
|
||||
console.log(`[Mermaid DEBUG] Render success for ${id}!`);
|
||||
} finally {
|
||||
if (document.body.contains(tempDiv)) {
|
||||
document.body.removeChild(tempDiv);
|
||||
@@ -277,30 +241,24 @@ const MermaidInternal: React.FC<MermaidProps> = ({
|
||||
scaledSvg = newSvgTag + rest;
|
||||
}
|
||||
|
||||
// Store SVG in React state — React renders it via dangerouslySetInnerHTML
|
||||
setSvgContent(scaledSvg);
|
||||
setIsRendered(true);
|
||||
setError(null);
|
||||
success = true;
|
||||
} catch (err) {
|
||||
console.warn(`Mermaid render attempt ${attempt} failed:`, err);
|
||||
if (attempt >= maxRetries) {
|
||||
console.error("Mermaid Render Error Final:", err);
|
||||
setError("Diagramm konnte nicht geladen werden (Render-Fehler).");
|
||||
setIsRendered(true);
|
||||
} else {
|
||||
// Wait before retrying (exponential backoff)
|
||||
await new Promise(resolve => setTimeout(resolve, attempt * 200));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Use ResizeObserver to trigger render ONLY when we have dimensions
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
if (entry.contentRect.width > 0 && !isRendered) {
|
||||
// Debounce slightly to ensure stable layout
|
||||
requestAnimationFrame(() => renderGraph());
|
||||
resizeObserver.disconnect();
|
||||
}
|
||||
@@ -309,7 +267,6 @@ const MermaidInternal: React.FC<MermaidProps> = ({
|
||||
|
||||
resizeObserver.observe(wrapperRef.current);
|
||||
|
||||
// Fallback: Try immediately if we already have size
|
||||
if (wrapperRef.current && wrapperRef.current.clientWidth > 0) {
|
||||
renderGraph();
|
||||
resizeObserver.disconnect();
|
||||
@@ -321,70 +278,68 @@ const MermaidInternal: React.FC<MermaidProps> = ({
|
||||
if (!id) return null;
|
||||
|
||||
return (
|
||||
<figure ref={wrapperRef} className="mermaid-wrapper not-prose my-20 w-full max-w-full border-y-2 border-slate-900 py-12 relative overflow-visible">
|
||||
<Reveal direction="up" delay={0.1}>
|
||||
<figure ref={wrapperRef} className="mermaid-wrapper not-prose my-16 w-full max-w-full group relative transition-all duration-500 ease-out z-10">
|
||||
|
||||
{/* Blueprint Grid Background Pattern */}
|
||||
<div className="absolute inset-0 z-0 pointer-events-none opacity-[0.03]" style={{ backgroundImage: 'linear-gradient(to right, #0f172a 1px, transparent 1px), linear-gradient(to bottom, #0f172a 1px, transparent 1px)', backgroundSize: '40px 40px' }} />
|
||||
<div className="absolute -inset-1 bg-gradient-to-r from-slate-200 to-slate-100/50 rounded-[2rem] blur opacity-20 group-hover:opacity-40 transition duration-1000 -z-10" />
|
||||
|
||||
<div className="w-full flex flex-col items-center justify-center relative z-10 px-4 md:px-8">
|
||||
{title && (
|
||||
<div className="w-full flex justify-between items-baseline mb-12 border-b border-slate-200 pb-4">
|
||||
<h4 className="text-left text-lg md:text-xl font-bold text-slate-900 tracking-tight m-0">
|
||||
{title}
|
||||
</h4>
|
||||
<span className="text-[10px] font-mono text-slate-400 uppercase tracking-[0.3em] font-bold">System_Architecture</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-center w-full overflow-x-auto">
|
||||
<div
|
||||
className={`mermaid
|
||||
w-full flex justify-center
|
||||
|
||||
/* Safely scale the SVG container wide without corrupting internal label calculations */
|
||||
[&>div]:!w-full [&>div]:!flex [&>div]:!justify-center
|
||||
[&_svg]:!max-w-full [&_svg]:max-w-4xl [&_svg]:!h-auto [&_svg]:!max-h-[60vh] md:[&_svg]:!max-h-[600px]
|
||||
|
||||
/* Premium Industrial Styling */
|
||||
[&_.node_rect]:!rx-[0px] [&_.node_rect]:!ry-[0px] /* Sharp corners for notebook look */
|
||||
[&_.node_rect]:!fill-white
|
||||
[&_.node_rect]:!stroke-slate-900 [&_.node_rect]:!stroke-[2px]
|
||||
[&_.node_rect]:!filter-none
|
||||
|
||||
[&_.edgePath_path]:!stroke-slate-900 [&_.edgePath_path]:!stroke-[2px]
|
||||
[&_.marker]:!fill-slate-900 [&_.marker]:!stroke-slate-900
|
||||
|
||||
/* Labels */
|
||||
[&_.nodeLabel]:!font-mono [&_.nodeLabel]:!font-bold [&_.nodeLabel]:!text-slate-900
|
||||
`}
|
||||
id={id}
|
||||
style={{
|
||||
maxWidth: "100%",
|
||||
overflow: "visible"
|
||||
}}
|
||||
>
|
||||
{error ? (
|
||||
<div className="text-red-500 p-4 border border-red-200 bg-red-50 text-sm font-mono uppercase tracking-widest text-center w-full h-32 flex items-center justify-center">
|
||||
{error}
|
||||
<div className="glass bg-white/80 backdrop-blur-xl border border-slate-100 rounded-2xl shadow-sm group-hover:shadow-md group-hover:border-slate-200 transition-all duration-500 overflow-hidden relative">
|
||||
|
||||
<div className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-white to-transparent opacity-80" />
|
||||
|
||||
<div className="w-full flex flex-col items-center justify-center p-6 md:p-8 lg:p-10 relative z-10">
|
||||
|
||||
{showShare && (
|
||||
<div className="absolute top-6 right-6 md:opacity-0 group-hover:opacity-100 transition-opacity duration-500 z-50">
|
||||
<ComponentShareButton targetId={id} title={title || 'System Architecture'} />
|
||||
</div>
|
||||
) : svgContent ? (
|
||||
<div dangerouslySetInnerHTML={{ __html: svgContent }} />
|
||||
) : (
|
||||
// Hide raw graph until rendered
|
||||
<div style={{ display: 'none' }}>{sanitizedGraph}</div>
|
||||
)}
|
||||
|
||||
{title && (
|
||||
<header className="w-full mb-8 flex flex-col md:flex-row md:justify-between md:items-end gap-2 border-b border-slate-100 pb-4">
|
||||
<div>
|
||||
<h4 className="text-xl md:text-2xl font-bold text-slate-900 tracking-tight m-0 flex items-center gap-3">
|
||||
<span className="w-2 h-2 rounded-full bg-slate-400 shadow-[0_0_8px_rgba(148,163,184,0.6)] hidden md:block" />
|
||||
{title}
|
||||
</h4>
|
||||
</div>
|
||||
</header>
|
||||
)}
|
||||
|
||||
<div className="flex justify-center w-full overflow-x-auto relative z-20">
|
||||
<div
|
||||
className={`mermaid
|
||||
w-full flex justify-center
|
||||
[&>div]:!w-full [&>div]:!flex [&>div]:!justify-center
|
||||
[&_svg]:!max-w-full [&_svg]:max-w-4xl [&_svg]:!h-auto [&_svg]:!max-h-[60vh] md:[&_svg]:!max-h-[600px]
|
||||
[&_.node_rect]:!rx-[8px] [&_.node_rect]:!ry-[8px]
|
||||
[&_.node_rect]:!fill-white
|
||||
[&_.node_rect]:!stroke-slate-200 [&_.node_rect]:!stroke-[2px]
|
||||
[&_.edgePath_path]:!stroke-slate-400 [&_.edgePath_path]:!stroke-[1.5px]
|
||||
[&_.marker]:!fill-slate-400 [&_.marker]:!stroke-slate-400
|
||||
[&_.nodeLabel]:!font-sans [&_.nodeLabel]:!font-bold [&_.nodeLabel]:!text-slate-700
|
||||
`}
|
||||
id={id}
|
||||
style={{
|
||||
maxWidth: "100%",
|
||||
overflow: "visible"
|
||||
}}
|
||||
>
|
||||
{error ? (
|
||||
<div className="text-red-500 p-4 border border-red-200 bg-red-50 text-xs font-mono uppercase tracking-widest text-center w-full h-32 flex items-center justify-center rounded-xl">
|
||||
{error}
|
||||
</div>
|
||||
) : svgContent ? (
|
||||
<div dangerouslySetInnerHTML={{ __html: svgContent }} />
|
||||
) : (
|
||||
<div style={{ display: 'none' }}>{sanitizedGraph}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{showShare && id && isRendered && (
|
||||
<div className="flex justify-end w-full mt-10">
|
||||
<DiagramShareButton
|
||||
diagramId={id}
|
||||
title={title}
|
||||
svgContent={svgContent}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</figure>
|
||||
</figure>
|
||||
</Reveal>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user