"use client";
import { useEffect, useRef, useState } from "react";
import { BrainCircuit, ChevronDown, Loader2 } from "lucide-react";
import { useTranslation } from "react-i18next";
import MarkdownRenderer from "./MarkdownRenderer";
interface ModelThinkingCardProps {
/** Inner text of a single ... block (already trimmed). */
content: string;
/**
* False while the model is still streaming inside the open tag, so
* the card stays expanded and shows a spinner. Once the closing tag arrives
* the card auto-collapses (default-folded) unless the user has pinned it
* open or closed manually.
*/
closed: boolean;
}
/**
* Collapsible card that surfaces a reasoning model's raw scratchpad
* in a way that visually echoes the system trace panels (subtle border,
* muted typography) without being mistaken for one.
*
* Behaviour:
* - Default-open while the model is still writing the scratchpad so the
* user can watch reasoning happen live.
* - Auto-collapses the moment the closing tag arrives.
* - Once the user toggles the card themselves, their preference wins for
* the rest of the message lifetime.
*/
export default function ModelThinkingCard({
content,
closed,
}: ModelThinkingCardProps) {
const { t } = useTranslation();
const [userToggled, setUserToggled] = useState(null);
const detailsRef = useRef(null);
const open = userToggled !== null ? userToggled : !closed;
// Keep the underlying element in sync with our derived `open`
// state. We do not bind `open` as a controlled prop because React strips
// the boolean attribute on `false`, which races with the browser's
// built-in toggle handling and produces a flicker the first time `closed`
// flips during streaming.
useEffect(() => {
const el = detailsRef.current;
if (el && el.open !== open) {
el.open = open;
}
}, [open]);
const handleToggle = (event: React.SyntheticEvent) => {
const next = event.currentTarget.open;
if (next !== open) {
setUserToggled(next);
}
};
const hasBody = content.trim().length > 0;
const placeholder = `${t("Thinking...")}`;
return (
{t("Model thinking")}
{!closed && (
)}
{hasBody ? (
) : (
{placeholder}
)}
);
}