Files
vocero-client/vocero-frontend/src/components/Bubbles.tsx
T

52 lines
2.1 KiB
TypeScript

import { memo, useEffect, useState } from "react";
import type { Message } from "../lib/messages";
import { AssistantMarkdown } from "./AssistantMarkdown";
function Bubble({ message, userName }: { message: Message; userName: string }) {
const [expiredText, setExpiredText] = useState<string | null>(null);
useEffect(() => {
if (message.partial) {
return;
}
const timer = setTimeout(() => {
setExpiredText(message.text);
}, 5000);
return () => {
clearTimeout(timer);
};
}, [message.text, message.partial]);
if (!message.partial && expiredText === message.text) {
return null;
}
return (
<div
className={`pointer-events-auto max-w-full rounded-panel border border-solid border-line bg-surface-raised px-3.5 py-2.5 text-[14px] leading-[1.5] wrap-anywhere shadow-[0_4px_18px_rgb(0_0_0/0.32)] compact:text-[13px] ${message.role === "assistant" ? "self-end" : "self-start"} ${message.role === "tool" ? "flex items-center gap-2 font-mono text-[13px] text-muted" : ""}`}
>
<div
className={`mb-1 font-mono text-[11px] font-medium tracking-[0.1em] uppercase opacity-70 compact:text-[10px] ${message.role === "user" ? "text-voice-user" : message.role === "assistant" ? "text-voice-assistant" : "text-voice-tool"}`}
>
{message.role === "user" ? userName : message.role === "tool" ? "Herramienta" : "Vocero"}
</div>
{message.role === "assistant" ? (
<AssistantMarkdown text={message.text} className="max-h-60 overflow-auto" />
) : (
<div className="max-h-60 overflow-auto">{message.role === "tool" ? message.toolName : message.text}</div>
)}
</div>
);
}
export const Bubbles = memo(function Bubbles({ messages, userName }: { messages: Message[]; userName: string }) {
return (
<div
className="pointer-events-none fixed top-24 right-7 z-100 flex w-[min(300px,calc(100vw-56px))] flex-col gap-2 compact:hidden"
aria-hidden="true"
inert
>
{messages.slice(-3).map((message) => (
<Bubble key={message.id} message={message} userName={userName} />
))}
</div>
);
});