"use client"; // Ürün genelindeki "el çizimi" mermaid şemalarının tek kaynağı: /meraklisina // hunisi de rehber yazılarındaki şemalar da bu bileşenle çizilir, böylece // görünüm (font, renk teması, çizgi titremesi) her yerde birebir aynı kalır. // Mermaid ağır bir paket olduğu için useEffect içinde dinamik import edilir; // sayfa paketine girmez. import { useEffect, useId, useRef, useState } from "react"; import localFont from "next/font/local"; // Şemanın "el çizimi" görünümüne uyan el yazısı fontu (Sweet Cucumber // Mocktail, Misti's Fonts). Türkçe glifler doğrulandı (ş, ğ, ı, İ… hepsi // var). DİKKAT: lisans "kişisel kullanım için ücretsiz" — canlıya çıkmadan // mistifonts.com/sweet-cucumber-mocktail adresinden ticari lisans alınmalı. const elYazisi = localFont({ src: "../../../fonts/SweetCucumberMocktail.woff2", display: "swap", }); export function ElCizimiSema({ tanim, ariaLabel, figcaption, }: { /** Mermaid tanımı (flowchart vb.) */ tanim: string; /** Şemanın tamamını anlatan erişilebilirlik metni */ ariaLabel: string; figcaption?: React.ReactNode; }) { const [svg, setSvg] = useState(null); // mermaid yüklenemez ya da çizemezse (bazı tarayıcılarda `getBBox` hatası) // sonsuz iskelet yerine metin karşılığı açık gelir const [hata, setHata] = useState(false); const figureRef = useRef(null); // mermaid.render'ın istediği DOM id'si: useId'nin ":" karakterleri geçersiz const semaId = `sema-${useId().replace(/[^a-zA-Z0-9-]/g, "")}`; // ~1 MB'lık mermaid mount'ta değil, şema görünürlüğe yaklaşınca insin — // şemalar hep fold altında (/meraklisina sayfa sonu, rehber makale ortası). useEffect(() => { const el = figureRef.current; if (!el) return; let iptal = false; const io = new IntersectionObserver( (girisler) => { if (!girisler.some((g) => g.isIntersecting)) return; io.disconnect(); cizdir(); }, // Kullanıcı şemaya varmadan yükleme başlasın { rootMargin: "200px 0px" }, ); io.observe(el); function cizdir() { import("mermaid") .then(async ({ default: mermaid }) => { mermaid.initialize({ startOnLoad: false, // Hata olursa mermaid kendi "bomba" SVG'sini basmasın; biz toparlıyoruz suppressErrorRendering: true, look: "handDrawn", handDrawnSeed: 7, // her render'da aynı çizgi titremesi (deterministik) theme: "base", fontFamily: `${elYazisi.style.fontFamily}, cursive`, themeVariables: { primaryColor: "#ffffff", primaryBorderColor: "#e2e8f0", primaryTextColor: "#0f172a", lineColor: "#94a3b8", edgeLabelBackground: "#ffffff", fontFamily: `${elYazisi.style.fontFamily}, cursive`, fontSize: "17px", // script fontun x-yüksekliği düşük; okunurluk için büyütüldü }, }); const sonuc = await mermaid.render(semaId, tanim); if (!iptal) setSvg(sonuc.svg); }) .catch(() => { // mermaid.render atarsa geçici ölçüm div'ini body'de bırakabiliyor document.getElementById(`d${semaId}`)?.remove(); if (!iptal) setHata(true); }); } return () => { iptal = true; io.disconnect(); }; }, [semaId, tanim]); return (
{svg ? (
) : hata ? null : (
)} {figcaption ? (
{figcaption}
) : null} {/* Şemanın metin karşılığı sunucu HTML'inde de durur: JS çalıştırmayan botlar ve şemayı çizemeyen tarayıcılar aynı içeriği okur. Kaynak yine tek `ariaLabel` prop'u — ikinci bir metin tutulmaz. */}
Şemayı metin olarak oku

{ariaLabel}

); }