aboutsummaryrefslogtreecommitdiffstats
path: root/renderer/src/components/ChatPane.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'renderer/src/components/ChatPane.tsx')
-rw-r--r--renderer/src/components/ChatPane.tsx66
1 files changed, 66 insertions, 0 deletions
diff --git a/renderer/src/components/ChatPane.tsx b/renderer/src/components/ChatPane.tsx
new file mode 100644
index 0000000..917d462
--- /dev/null
+++ b/renderer/src/components/ChatPane.tsx
@@ -0,0 +1,66 @@
1import React, { useState, useRef, useEffect } from "react";
2import type { Message } from "../types";
3
4interface ChatPaneProps {
5 messages: Message[];
6 onSend: (message: string) => void;
7 isLoading: boolean;
8 disabled: boolean;
9 placeholder: string;
10}
11
12export function ChatPane({
13 messages,
14 onSend,
15 isLoading,
16 disabled,
17 placeholder,
18}: ChatPaneProps) {
19 const [input, setInput] = useState("");
20 const messagesEndRef = useRef<HTMLDivElement>(null);
21
22 useEffect(() => {
23 messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
24 }, [messages]);
25
26 const handleSend = () => {
27 if (!input.trim() || isLoading || disabled) return;
28 onSend(input.trim());
29 setInput("");
30 };
31
32 return (
33 <div className="chat-pane">
34 <div className="chat-messages">
35 {messages.map((msg) => (
36 <div key={msg.id} className={`message ${msg.role}`}>
37 <div className="message-content">{msg.content}</div>
38 </div>
39 ))}
40 {isLoading && (
41 <div className="message assistant loading">
42 <div className="message-content">Thinking...</div>
43 </div>
44 )}
45 <div ref={messagesEndRef} />
46 </div>
47
48 <div className="chat-input">
49 <input
50 type="text"
51 value={input}
52 onChange={(e) => setInput(e.target.value)}
53 onKeyDown={(e) => e.key === "Enter" && !e.shiftKey && handleSend()}
54 placeholder={placeholder}
55 disabled={disabled || isLoading}
56 />
57 <button
58 onClick={handleSend}
59 disabled={disabled || isLoading || !input.trim()}
60 >
61 Send
62 </button>
63 </div>
64 </div>
65 );
66}