File size: 3,411 Bytes
39e9c8d
a9df765
 
035ec20
 
 
 
 
 
adfa6d0
39e9c8d
 
 
 
 
 
 
 
 
 
035ec20
 
 
 
 
 
 
 
39e9c8d
 
 
 
 
035ec20
39e9c8d
 
a9df765
6e94481
39e9c8d
adfa6d0
 
 
 
 
 
 
 
 
6e94481
 
adfa6d0
 
 
 
 
 
6e94481
 
 
 
 
 
 
 
 
 
035ec20
39e9c8d
 
035ec20
39e9c8d
 
 
 
 
 
 
 
a9df765
9ebaf61
39e9c8d
adfa6d0
 
 
 
 
 
 
 
 
6e94481
39e9c8d
 
 
 
035ec20
39e9c8d
 
a7e4d75
035ec20
 
 
39e9c8d
a9df765
 
 
 
 
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
import { useState, useRef, useEffect } from 'react';
import './ChatPanel.css';

/**
 * ChatPanel
 * 
 * Layer 3: Interaction (Utility Layer)
 * Handles live conversation and input.
 */
function ChatPanel({ messages, onSendMessage, onNewChat, userAvatar, isTyping, onInputStateChange }) {
  const [input, setInput] = useState('');
  const scrollRef = useRef(null);

  // Auto-scroll to bottom on new messages
  useEffect(() => {
    if (scrollRef.current) {
      scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
    }
  }, [messages, isTyping]);

  const handleInputChange = (e) => {
    const val = e.target.value;
    setInput(val);
    if (onInputStateChange) {
      onInputStateChange(val.length > 0);
    }
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!input.trim() || isTyping) return;
    onSendMessage(input);
    setInput('');
    if (onInputStateChange) onInputStateChange(false);
  };

  return (
    <div className="chat-interface">
      <div className="chat-messages" ref={scrollRef}>
        {messages.length === 0 && (
          <div className="welcome-state">
            <div className="welcome-icon">
              <span className="material-icons">lens_blur</span>
            </div>
            <h3>Neural Core Initialized.</h3>
            <p>Feed me a thought to begin building your neural pathways.</p>
          </div>
        )}
        {messages.map((msg, i) => (
          <div key={i} className={`chat-bubble-group ${msg.role}`}>
            <div className="chat-avatar">
              {msg.role === 'user' ? (
                <img src={userAvatar} alt="User" />
              ) : (
                <span className="material-icons">lens_blur</span>
              )}
            </div>
            <div className="bubble-body">
              <div className="bubble-meta">
                <strong>{msg.role === 'user' ? 'You' : 'Soma'}</strong>
              </div>
              <div className="bubble-text">{msg.content}</div>
              <div className="bubble-time">{msg.timestamp}</div>
            </div>
          </div>
        ))}
        {isTyping && messages.length > 0 && messages[messages.length - 1].role === 'user' && (
          <div className="chat-bubble-group soma typing">
             <div className="chat-avatar">
                <span className="material-icons">lens_blur</span>
             </div>
             <div className="bubble-body">
                <div className="typing-dots">
                   <span></span><span></span><span></span>
                </div>
             </div>
          </div>
        )}
      </div>

      <form className="chat-input-row" onSubmit={handleSubmit}>
        <button 
          className="new-chat-btn" 
          type="button" 
          onClick={onNewChat}
          title="New Chat"
          disabled={isTyping}
        >
          <span className="material-icons">add</span>
        </button>
        <div className="input-field-wrap">
          <input 
            type="text" 
            placeholder="Message Soma..." 
            value={input}
            onChange={handleInputChange}
            disabled={isTyping}
          />
        </div>
        <button className="send-btn" type="submit" disabled={!input.trim() || isTyping}>
          <span className="material-icons">north</span>
        </button>
      </form>
    </div>
  );
}

export default ChatPanel;