Files
edbridge-scholars/src/components/RenderQuestionText.tsx
2026-03-12 02:39:34 +06:00

81 lines
2.4 KiB
TypeScript

import { Component, type ReactNode } from "react";
// @ts-ignore
import { BlockMath, InlineMath } from "react-katex";
// ─── Error boundary ───────────────────────────────────────────────────────────
// react-katex throws synchronously during render for invalid LaTeX, so a class
// error boundary is the only reliable way to catch it.
interface MathErrorBoundaryProps {
raw: string; // the original LaTeX string, shown as fallback
children: ReactNode;
}
interface MathErrorBoundaryState {
failed: boolean;
}
export class MathErrorBoundary extends Component<
MathErrorBoundaryProps,
MathErrorBoundaryState
> {
state: MathErrorBoundaryState = { failed: false };
static getDerivedStateFromError(): MathErrorBoundaryState {
return { failed: true };
}
render() {
if (this.state.failed) {
return (
<span
title={`Could not render: ${this.props.raw}`}
style={{
fontFamily: "monospace",
background: "rgba(239,68,68,0.08)",
border: "1px solid rgba(239,68,68,0.3)",
borderRadius: 4,
padding: "0 4px",
color: "#f87171",
fontSize: "0.9em",
}}
>
{this.props.raw}
</span>
);
}
return this.props.children;
}
}
// ─── Renderer ─────────────────────────────────────────────────────────────────
export const renderQuestionText = (text: string) => {
if (!text) return null;
const parts = text.split(/(\$\$.*?\$\$|\$.*?\$)/gs);
return (
<>
{parts.map((part, index) => {
if (part.startsWith("$$")) {
const latex = part.slice(2, -2);
return (
<MathErrorBoundary key={index} raw={part}>
<BlockMath>{latex}</BlockMath>
</MathErrorBoundary>
);
}
if (part.startsWith("$")) {
const latex = part.slice(1, -1);
return (
<MathErrorBoundary key={index} raw={part}>
<InlineMath>{latex}</InlineMath>
</MathErrorBoundary>
);
}
return <span key={index}>{part}</span>;
})}
</>
);
};