turash/bugulma/frontend/components/chatbot/ChatHistory.tsx
Damir Mukimov 6347f42e20
Consolidate repositories: Remove nested frontend .git and merge into main repository
- Remove nested git repository from bugulma/frontend/.git
- Add all frontend files to main repository tracking
- Convert from separate frontend/backend repos to unified monorepo
- Preserve all frontend code and development history as tracked files
- Eliminate nested repository complexity for simpler development workflow

This creates a proper monorepo structure with frontend and backend
coexisting in the same repository for easier development and deployment.
2025-11-25 06:02:57 +01:00

73 lines
2.6 KiB
TypeScript

import React from 'react';
import { ChatMessage } from '@/types.ts';
import { useTranslation } from '@/hooks/useI18n.tsx';
import TypingIndicator from '@/components/chatbot/TypingIndicator.tsx';
import MarkdownRenderer from '@/components/chatbot/MarkdownRenderer.tsx';
import Button from '@/components/ui/Button.tsx';
import { Check, Copy } from 'lucide-react';
interface ChatHistoryProps {
messages: ChatMessage[];
messagesEndRef: React.RefObject<HTMLDivElement>;
copiedText: string | null;
onCopy: (text: string) => void;
}
const ChatHistory = ({ messages, messagesEndRef, copiedText, onCopy }: ChatHistoryProps) => {
const { t } = useTranslation();
return (
<div className="flex-1 p-4 overflow-y-auto space-y-4">
{messages.map((msg) => (
<div
key={msg.id}
className={`flex items-end gap-2 group ${msg.role === 'user' ? 'justify-end' : ''}`}
>
{msg.role === 'model' && (
<div className="h-8 w-8 rounded-full bg-primary text-primary-foreground flex items-center justify-center font-bold text-sm shrink-0 self-start">
{t('chatbot.aiAcronym')}
</div>
)}
<div
className={`max-w-[calc(100%-3rem)] p-3 rounded-2xl ${msg.role === 'user' ? 'bg-primary text-primary-foreground rounded-br-none' : 'bg-muted rounded-bl-none'}`}
>
{msg.imageUrl && (
<div className="mb-2 rounded-lg overflow-hidden border">
<img
src={msg.imageUrl}
alt="Chat image"
className="max-w-full h-auto"
loading="lazy"
decoding="async"
onError={(e) => {
(e.target as HTMLImageElement).style.display = 'none';
}}
/>
</div>
)}
{msg.isLoading ? <TypingIndicator /> : <MarkdownRenderer text={msg.text} />}
</div>
{msg.role === 'model' && !msg.isLoading && msg.text && (
<Button
variant="ghost"
size="sm"
className="p-1.5 h-7 w-7 opacity-0 group-hover:opacity-100 transition-opacity"
onClick={() => onCopy(msg.text)}
aria-label={t('chatbot.copyLabel')}
>
{copiedText === msg.text ? (
<Check className="h-4 text-current text-success w-4" />
) : (
<Copy className="h-4 text-current w-4" />
)}
</Button>
)}
</div>
))}
<div ref={messagesEndRef} />
</div>
);
};
export default React.memo(ChatHistory);