98 lines
2.8 KiB
TypeScript
98 lines
2.8 KiB
TypeScript
import { ChevronDown } from "lucide-react";
|
|
import type { ReactNode, Ref } from "react";
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface ThinkingReasoningShellProps {
|
|
active: boolean;
|
|
expanded: boolean;
|
|
label: string;
|
|
children: ReactNode;
|
|
viewportRef: Ref<HTMLDivElement>;
|
|
contentRef: Ref<HTMLDivElement>;
|
|
fadeTop: boolean;
|
|
fadeBottom: boolean;
|
|
onToggle: () => void;
|
|
onScroll: () => void;
|
|
}
|
|
|
|
export function ThinkingReasoningShell({
|
|
active,
|
|
expanded,
|
|
label,
|
|
children,
|
|
viewportRef,
|
|
contentRef,
|
|
fadeTop,
|
|
fadeBottom,
|
|
onToggle,
|
|
onScroll,
|
|
}: ThinkingReasoningShellProps) {
|
|
return (
|
|
<div
|
|
className="flex w-full max-w-[45rem] animate-in flex-col fade-in duration-300 motion-reduce:animate-none"
|
|
data-state={active ? "thinking" : "done"}
|
|
>
|
|
<button
|
|
type="button"
|
|
data-thread-disclosure=""
|
|
className="group inline-flex min-h-5 items-center self-start gap-1.5 bg-transparent p-0"
|
|
onClick={onToggle}
|
|
aria-expanded={expanded}
|
|
aria-label={label}
|
|
aria-live={active ? "polite" : undefined}
|
|
>
|
|
<span
|
|
className={cn(
|
|
"min-w-0 truncate text-[13px] font-medium leading-[18px] text-muted-foreground/70",
|
|
active && "animate-pulse motion-reduce:animate-none",
|
|
)}
|
|
>
|
|
{label}
|
|
</span>
|
|
<span
|
|
className={cn(
|
|
"inline-flex shrink-0 transition-transform [transition-duration:220ms] ease-out",
|
|
"motion-reduce:transition-none",
|
|
expanded && "rotate-180",
|
|
)}
|
|
>
|
|
<ChevronDown
|
|
className={cn(
|
|
"h-3 w-3 text-muted-foreground/60 transition-colors duration-200",
|
|
"group-hover:text-muted-foreground motion-reduce:transition-none",
|
|
)}
|
|
strokeWidth={1.8}
|
|
aria-hidden
|
|
/>
|
|
</span>
|
|
</button>
|
|
|
|
<div
|
|
className={cn(
|
|
"grid transition-[grid-template-rows,opacity] [transition-duration:220ms] ease-out motion-reduce:transition-none",
|
|
expanded
|
|
? "grid-rows-[1fr] opacity-100"
|
|
: "pointer-events-none grid-rows-[0fr] opacity-0",
|
|
)}
|
|
>
|
|
<div className="min-h-0 overflow-hidden">
|
|
<div
|
|
ref={viewportRef}
|
|
data-testid={expanded ? "agent-activity-scroll" : undefined}
|
|
data-fade-top={fadeTop}
|
|
data-fade-bottom={fadeBottom}
|
|
onScroll={onScroll}
|
|
className="activity-scroll-fade mt-1.5 max-h-[180px] overflow-y-auto pr-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
|
aria-hidden={!expanded}
|
|
>
|
|
<div ref={contentRef} className="flex flex-col gap-0.5">
|
|
{children}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|