Cleanup UI bubbles

This commit is contained in:
Nicolas Mowen 2026-02-18 16:34:51 -07:00
parent 5faf5e0d84
commit b65ae76f0c
2 changed files with 63 additions and 43 deletions

View File

@ -33,11 +33,12 @@ export function ToolCallBubble({
return ( return (
<div <div
className={ className={cn(
"rounded-lg px-3 py-2",
isLeft isLeft
? "self-start rounded-lg bg-muted px-3 py-2" ? "self-start bg-muted"
: "self-end rounded-lg bg-primary px-3 py-2 text-primary-foreground" : "self-end bg-primary text-primary-foreground",
} )}
> >
<Collapsible open={open} onOpenChange={setOpen}> <Collapsible open={open} onOpenChange={setOpen}>
<CollapsibleTrigger asChild> <CollapsibleTrigger asChild>
@ -66,7 +67,7 @@ export function ToolCallBubble({
<div className="font-medium text-muted-foreground"> <div className="font-medium text-muted-foreground">
{t("arguments")} {t("arguments")}
</div> </div>
<pre className="mt-1 max-h-32 overflow-auto whitespace-pre-wrap break-words rounded bg-muted/50 p-2 text-[10px]"> <pre className="scrollbar-container mt-1 max-h-32 overflow-auto whitespace-pre-wrap break-words rounded bg-muted/50 p-2 text-[10px]">
{JSON.stringify(args, null, 2)} {JSON.stringify(args, null, 2)}
</pre> </pre>
</div> </div>
@ -74,7 +75,7 @@ export function ToolCallBubble({
{!isLeft && response && response !== "" && ( {!isLeft && response && response !== "" && (
<div className="text-xs"> <div className="text-xs">
<div className="font-medium opacity-80">{t("response")}</div> <div className="font-medium opacity-80">{t("response")}</div>
<pre className="mt-1 max-h-32 overflow-auto whitespace-pre-wrap break-words rounded bg-primary/20 p-2 text-[10px]"> <pre className="scrollbar-container mt-1 max-h-32 overflow-auto whitespace-pre-wrap break-words rounded bg-primary/20 p-2 text-[10px]">
{response} {response}
</pre> </pre>
</div> </div>

View File

@ -75,7 +75,17 @@ export default function ChatPage() {
<div className="flex size-full justify-center p-2"> <div className="flex size-full justify-center p-2">
<div className="flex size-full flex-col xl:w-[50%] 3xl:w-[35%]"> <div className="flex size-full flex-col xl:w-[50%] 3xl:w-[35%]">
<div className="scrollbar-container flex min-h-0 w-full flex-1 flex-col gap-2 overflow-y-auto"> <div className="scrollbar-container flex min-h-0 w-full flex-1 flex-col gap-2 overflow-y-auto">
{messages.map((msg, i) => ( {messages.map((msg, i) => {
const isStreamingPlaceholder =
i === messages.length - 1 &&
msg.role === "assistant" &&
isLoading &&
!msg.content?.trim() &&
!(msg.toolCalls && msg.toolCalls.length > 0);
if (isStreamingPlaceholder) {
return <div key={i} />;
}
return (
<div key={i} className="flex flex-col gap-2"> <div key={i} className="flex flex-col gap-2">
{msg.role === "assistant" && msg.toolCalls && ( {msg.role === "assistant" && msg.toolCalls && (
<> <>
@ -109,12 +119,21 @@ export default function ChatPage() {
} }
/> />
</div> </div>
))} );
{isLoading && ( })}
{(() => {
const lastMsg = messages[messages.length - 1];
const showProcessing =
isLoading &&
lastMsg?.role === "assistant" &&
!lastMsg.content?.trim() &&
!(lastMsg.toolCalls && lastMsg.toolCalls.length > 0);
return showProcessing ? (
<div className="self-start rounded-lg bg-muted px-3 py-2 text-muted-foreground"> <div className="self-start rounded-lg bg-muted px-3 py-2 text-muted-foreground">
{t("processing")} {t("processing")}
</div> </div>
)} ) : null;
})()}
{error && ( {error && (
<p className="self-start text-sm text-destructive" role="alert"> <p className="self-start text-sm text-destructive" role="alert">
{error} {error}