- Add SessionManager class to handle PTY sessions with WebSocket connections. - Implement methods for creating, retrieving, and destroying sessions. - Handle PTY output and WebSocket messages for terminal interaction. - Ensure graceful session destruction and cleanup. feat: initialize web application with Next.js and Tailwind CSS - Create initial Next.js application structure with TypeScript support. - Set up Tailwind CSS for styling with custom theme configurations. - Add ESLint configuration for code quality and consistency. feat: implement chat API and UI components - Create chat API route to handle chat requests and responses. - Develop chat layout with sidebar, header, chat window, and input components. - Integrate Zustand for state management of conversations and messages. - Add utility functions for formatting dates and managing class names. chore: add environment variables and configuration files - Create .env.example for environment variable setup. - Add configuration files for PostCSS, Tailwind CSS, and TypeScript. - Set up package.json with necessary dependencies and scripts for development.
74 lines
2.0 KiB
TypeScript
74 lines
2.0 KiB
TypeScript
"use client";
|
|
|
|
import { User, Bot, AlertCircle } from "lucide-react";
|
|
import { cn, extractTextContent } from "@/lib/utils";
|
|
import type { Message } from "@/lib/types";
|
|
import { MarkdownContent } from "./MarkdownContent";
|
|
|
|
interface MessageBubbleProps {
|
|
message: Message;
|
|
}
|
|
|
|
export function MessageBubble({ message }: MessageBubbleProps) {
|
|
const isUser = message.role === "user";
|
|
const isError = message.status === "error";
|
|
const text = extractTextContent(message.content);
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"flex gap-3 animate-fade-in",
|
|
isUser && "flex-row-reverse"
|
|
)}
|
|
>
|
|
{/* Avatar */}
|
|
<div
|
|
className={cn(
|
|
"w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0 mt-0.5",
|
|
isUser
|
|
? "bg-brand-600 text-white"
|
|
: isError
|
|
? "bg-red-900 text-red-300"
|
|
: "bg-surface-700 text-surface-300"
|
|
)}
|
|
>
|
|
{isUser ? (
|
|
<User className="w-4 h-4" />
|
|
) : isError ? (
|
|
<AlertCircle className="w-4 h-4" />
|
|
) : (
|
|
<Bot className="w-4 h-4" />
|
|
)}
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div
|
|
className={cn(
|
|
"flex-1 min-w-0 max-w-2xl",
|
|
isUser && "flex justify-end"
|
|
)}
|
|
>
|
|
<div
|
|
className={cn(
|
|
"rounded-2xl px-4 py-3 text-sm",
|
|
isUser
|
|
? "bg-brand-600 text-white rounded-tr-sm"
|
|
: isError
|
|
? "bg-red-950 border border-red-800 text-red-200 rounded-tl-sm"
|
|
: "bg-surface-800 text-surface-100 rounded-tl-sm"
|
|
)}
|
|
>
|
|
{isUser ? (
|
|
<p className="whitespace-pre-wrap break-words">{text}</p>
|
|
) : (
|
|
<MarkdownContent content={text} />
|
|
)}
|
|
{message.status === "streaming" && (
|
|
<span className="inline-block w-1.5 h-4 bg-current ml-0.5 animate-pulse-soft" />
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|