File size: 4,744 Bytes
59b210a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
'use client'

import { useState, useEffect, useRef } from 'react'
import { Copy, User } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { motion } from 'framer-motion'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import ReactMarkdown from 'react-markdown' // For rendering markdown content
import remarkGfm from 'remark-gfm' // To support GitHub-flavored markdown (e.g., tables, strikethrough, etc.)

export default function ChatArea({ messages, chatAreaRef }) {
  const [copiedId, setCopiedId] = useState(null) // State to track the copied message
  const bottomRef = useRef(null) // Ref for auto-scroll to bottom

  // Function to copy message content to the clipboard
  const handleCopy = (content, id) => {
    navigator.clipboard.writeText(content)
    setCopiedId(id)
    setTimeout(() => setCopiedId(null), 2000) // Reset copied state after 2 seconds
  }

  // Automatically scroll to the bottom whenever new messages arrive
  useEffect(() => {
    bottomRef.current?.scrollIntoView({ behavior: 'smooth' })
  }, [messages])

  return (
    <div
      ref={chatAreaRef}
      className="flex-1 overflow-y-auto overflow-x-auto p-4 space-y-4" // Allows both horizontal and vertical scrolling
      style={{ scrollBehavior: 'smooth' }} // Smooth scrolling for better UX
    >
      {messages.map((message) => (
        <motion.div
          key={message.id}
          initial={{ opacity: 0, y: 50 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.5 }}
          className={`flex ${message.sender === 'user' ? 'justify-end' : 'justify-start'}`}
        >
          <div
            className={`flex items-start space-x-2 max-w-3xl ${message.sender === 'user' ? 'flex-row-reverse' : ''}`}
          >
            {/* Avatar for user or bot */}
            <Avatar className="w-14 h-14">
              {message.sender === 'user' ? (
                <AvatarImage src="/yy.png" alt="User" />
              ) : (
                <AvatarImage src="/kk.png" alt="Karma Bot" />
              )}
              <AvatarFallback>{message.sender === 'user' ? <User /> : 'K'}</AvatarFallback>
            </Avatar>

            {/* Message container */}
            <div
              className={`p-4 rounded-lg transition-all duration-300 ease-in-out hover:shadow-lg ${message.sender === 'user' ? 'bg-primary text-primary-foreground' : 'bg-secondary'
                }`}
            >
              <div className="flex justify-between items-start mb-2">
                <span className="font-bold">{message.sender === 'user' ? 'You' : 'Karma'}</span>
                {/* Copy Button */}

                {/* Copy confirmation */}
                {copiedId === message.id ? (
                  <span className="text-xs text-green-500 mt-1">Copied ✅</span>
                ) : (
                  <Button
                    variant="ghost"
                    size="icon"
                    onClick={() => handleCopy(message.imageUrl ?? message.content, message.id)}
                  >
                    <Copy className="h-4 w-4" />
                  </Button>)}
              </div>


              {/* Image rendering with link */}
              {message.imageUrl && (
                <div className="mt-2">
                  <a
                    href={message.imageUrl}
                    target="_blank"
                    rel="noopener noreferrer"
                    title={message.content || 'Generated image'}
                  >
                    <img
                      src={message.imageUrl}
                      alt="Generated or uploaded image"
                      className="max-w-full h-auto rounded-lg"
                    />
                  </a>
                  {message.imageText && (
                    <p className="mt-1 text-sm text-muted-foreground">{message.imageText}</p>
                  )}
                </div>
              )}

              {/* Markdown rendering for message content */}
              {message.content && (
                <div className="mt-2 prose prose-sm text-muted-foreground">
                  <ReactMarkdown remarkPlugins={[remarkGfm]}>
                    {message.content}
                  </ReactMarkdown>
                </div>
              )}

              {/* Metadata (model and timestamp) */}
              <div className="mt-2 text-xs text-muted-foreground">
                <p>model: {message.model?.description || 'N/A'} [{new Date(message.timestamp).toLocaleString()}] - {message.seed}</p>
              </div>


            </div>
          </div>
        </motion.div>
      ))}
      {/* Ref for auto-scroll to bottom */}
      <div ref={bottomRef} />
    </div>
  )
}