fix(rehber): şema metni SSR'da + mermaid hata toparlama
Şemanın metin karşılığı yalnızca SVG çizilince `aria-label` olarak basılıyordu; JS çalıştırmayan botlar iskeletten başka bir şey görmüyordu. Artık aynı `ariaLabel` metni figure içinde bir `<details>` olarak sunucu HTML'ine girer. mermaid import/render hatası yakalanır: iskelet kalkar, metin açık gelir, mermaid'in body'de bıraktığı geçici div temizlenir. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,9 @@ export function ElCizimiSema({
|
||||
figcaption?: React.ReactNode;
|
||||
}) {
|
||||
const [svg, setSvg] = useState<string | null>(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<HTMLElement>(null);
|
||||
// mermaid.render'ın istediği DOM id'si: useId'nin ":" karakterleri geçersiz
|
||||
const semaId = `sema-${useId().replace(/[^a-zA-Z0-9-]/g, "")}`;
|
||||
@@ -52,26 +55,34 @@ export function ElCizimiSema({
|
||||
io.observe(el);
|
||||
|
||||
function cizdir() {
|
||||
import("mermaid").then(async ({ default: mermaid }) => {
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
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",
|
||||
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`,
|
||||
fontSize: "17px", // script fontun x-yüksekliği düşük; okunurluk için büyütüldü
|
||||
},
|
||||
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);
|
||||
});
|
||||
const sonuc = await mermaid.render(semaId, tanim);
|
||||
if (!iptal) setSvg(sonuc.svg);
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
@@ -89,7 +100,7 @@ export function ElCizimiSema({
|
||||
className={`${elYazisi.className} overflow-x-auto [&_svg]:mx-auto [&_svg]:h-auto [&_svg]:max-w-full`}
|
||||
dangerouslySetInnerHTML={{ __html: svg }}
|
||||
/>
|
||||
) : (
|
||||
) : hata ? null : (
|
||||
<div
|
||||
aria-hidden
|
||||
className="h-96 animate-pulse rounded-xl bg-slate-100"
|
||||
@@ -100,6 +111,18 @@ export function ElCizimiSema({
|
||||
{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. */}
|
||||
<details
|
||||
open={hata || undefined}
|
||||
className="mt-2 text-xs text-slate-500"
|
||||
>
|
||||
<summary className="cursor-pointer font-medium text-slate-600">
|
||||
Şemayı metin olarak oku
|
||||
</summary>
|
||||
<p className="mt-2 text-sm leading-6 text-slate-600">{ariaLabel}</p>
|
||||
</details>
|
||||
</figure>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user