@aircall/blocks
Version:
Aircall Blocks — higher-level UI compositions built on @aircall/ds
79 lines (78 loc) • 2.68 kB
JavaScript
import { t as ChatbotLoadingBlock } from "./chatbot-loading-block-DCJoZhC2.js";
import { Alert, AlertDescription, AlertTitle, useDsTranslation, useTheme } from "@aircall/ds";
import { useEffect, useId, useRef, useState } from "react";
import { jsx, jsxs } from "react/jsx-runtime";
import { CircleX } from "@aircall/react-icons";
import mermaid from "mermaid";
//#region src/components/chatbot-mermaid-block.tsx
const STREAM_DEBOUNCE_MS = 500;
/** Renders a fenced ```mermaid code block inside a chatbot response as an SVG diagram. */
function ChatbotMermaidBlock({ code, streaming = false }) {
const t = useDsTranslation("blocks");
const { resolvedTheme } = useTheme();
const diagramId = useId().replace(/:/g, "-");
const renderCountRef = useRef(0);
const containerRef = useRef(null);
const [svg, setSvg] = useState(null);
const [bindFunctions, setBindFunctions] = useState();
const [error, setError] = useState(false);
useEffect(() => {
if (resolvedTheme === void 0) return;
let cancelled = false;
function attemptRender() {
mermaid.initialize({
startOnLoad: false,
theme: resolvedTheme === "dark" ? "dark" : "default",
securityLevel: "strict",
suppressErrorRendering: true
});
setError(false);
renderCountRef.current += 1;
const renderId = `mermaid-${diagramId}-${renderCountRef.current}`;
mermaid.render(renderId, code).then((result) => {
if (cancelled) return;
setSvg(result.svg);
setBindFunctions(() => result.bindFunctions);
}).catch(() => {
if (cancelled) return;
if (!streaming) setError(true);
});
}
if (!streaming) {
attemptRender();
return () => {
cancelled = true;
};
}
const timeout = setTimeout(attemptRender, STREAM_DEBOUNCE_MS);
return () => {
cancelled = true;
clearTimeout(timeout);
};
}, [
code,
diagramId,
resolvedTheme,
streaming
]);
useEffect(() => {
if (svg && containerRef.current) bindFunctions === null || bindFunctions === void 0 || bindFunctions(containerRef.current);
}, [svg, bindFunctions]);
if (error) return /* @__PURE__ */ jsxs(Alert, {
variant: "error",
className: "mt-4 first:mt-0",
children: [
/* @__PURE__ */ jsx(CircleX, { "aria-hidden": "true" }),
/* @__PURE__ */ jsx(AlertTitle, { children: t("chatbotResponse.mermaidErrorTitle") }),
/* @__PURE__ */ jsx(AlertDescription, { children: t("chatbotResponse.mermaidErrorDescription") })
]
});
if (!svg) return /* @__PURE__ */ jsx(ChatbotLoadingBlock, {});
return /* @__PURE__ */ jsx("div", {
ref: containerRef,
className: "mt-4 overflow-auto first:mt-0",
dangerouslySetInnerHTML: { __html: svg }
});
}
//#endregion
export { ChatbotMermaidBlock };