81 lines
2.4 KiB
TypeScript
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>;
|
|
})}
|
|
</>
|
|
);
|
|
};
|