| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546 |
- import { useTranslation } from "react-i18next";
- import { cn } from "@/lib/utils";
- import type { Message } from "@/types";
- export function MessageBubble({ message }: { message: Message }) {
- const { t } = useTranslation();
- const isUser = message.role === "user";
- const isSystem = message.role === "system";
- const content = readableContent(message, t);
- return (
- <div className={cn("flex min-w-0", isUser ? "justify-end" : isSystem ? "justify-center" : "justify-start")}>
- <div
- className={cn(
- "min-w-0 max-w-[82%] rounded-md border px-3 py-2 text-sm",
- isUser && "border-primary/30 bg-primary text-primary-foreground",
- !isUser && !isSystem && "border-border bg-surface-elevated",
- isSystem && "max-w-[90%] border-border bg-muted/50 text-muted-foreground")}
- >
- {!isSystem ? (
- <div className={cn("mb-0.5 text-[11px] font-medium uppercase tracking-wide", isUser ? "text-primary-foreground/70" : "text-muted-foreground")}>
- {isUser ? t("sessions.roleUser") : t("sessions.roleAssistant")}
- </div>
- ) : null}
- <p className="whitespace-pre-wrap break-words leading-6 [overflow-wrap:anywhere]">{content}</p>
- </div>
- </div>
- );
- }
- function readableContent(message: Message, t: (key: string, options?: Record<string, unknown>) => string) {
- const text = message.content_text?.trim();
- if (text) return text;
- const payload = message.content_json;
- if (!payload) return t("sessions.noTextContent");
- if (typeof payload === "string") return payload;
- if (Array.isArray(payload)) return t("sessions.structuredItems", { count: payload.length });
- if (typeof payload === "object") {
- const record = payload as Record<string, unknown>;
- for (const key of ["text", "message", "answer", "output", "result", "content"]) {
- const value = record[key];
- if (typeof value === "string" && value.trim()) return value;
- }
- return t("sessions.structuredMessage");
- }
- return String(payload);
- }
|