CloneChatPage.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import React, { useState, useRef, useEffect } from 'react';
  2. import { useNavigate } from 'react-router-dom';
  3. interface Message {
  4. id: number;
  5. text: string;
  6. sender: 'user' | 'clone';
  7. }
  8. const CloneChatPage: React.FC = () => {
  9. const navigate = useNavigate();
  10. const [messages, setMessages] = useState<Message[]>([
  11. { id: 1, text: "Hello! You can start training me by talking to me. What's on your mind?", sender: 'clone' }
  12. ]);
  13. const [inputValue, setInputValue] = useState('');
  14. const [isTyping, setIsTyping] = useState(false);
  15. const messagesEndRef = useRef<HTMLDivElement>(null);
  16. const scrollToBottom = () => {
  17. messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
  18. };
  19. useEffect(scrollToBottom, [messages, isTyping]);
  20. const handleSendMessage = (e: React.FormEvent) => {
  21. e.preventDefault();
  22. if (inputValue.trim() === '') return;
  23. const userMessage: Message = {
  24. id: Date.now(),
  25. text: inputValue,
  26. sender: 'user',
  27. };
  28. setMessages(prev => [...prev, userMessage]);
  29. setInputValue('');
  30. setIsTyping(true);
  31. // Simulate clone response
  32. setTimeout(() => {
  33. const cloneResponse: Message = {
  34. id: Date.now() + 1,
  35. text: `That's an interesting point. It reminds me of how you once said... (simulated response based on your persona).`,
  36. sender: 'clone',
  37. };
  38. setIsTyping(false);
  39. setMessages(prev => [...prev, cloneResponse]);
  40. }, 1500 + Math.random() * 1000);
  41. };
  42. return (
  43. <div className="flex flex-col h-[calc(100vh-60px)] max-w-3xl mx-auto bg-white rounded-2xl overflow-hidden border border-slate-200">
  44. {/* Header */}
  45. <header className="flex items-center justify-between p-4 border-b border-slate-200 bg-slate-50 flex-shrink-0">
  46. <h1 className="text-xl font-bold text-slate-800">Train My Digital Clone</h1>
  47. <button
  48. onClick={() => navigate('/edit')}
  49. className="px-4 py-2 text-sm font-medium text-slate-700 bg-white border border-slate-300 rounded-md hover:bg-slate-100"
  50. >
  51. Back to Edit
  52. </button>
  53. </header>
  54. {/* Chat Area */}
  55. <main className="flex-1 overflow-y-auto p-6 space-y-4">
  56. {messages.map(msg => (
  57. <div key={msg.id} className={`flex items-end gap-2 ${msg.sender === 'user' ? 'justify-end' : 'justify-start'}`}>
  58. {msg.sender === 'clone' && (
  59. <div className="w-8 h-8 rounded-full bg-gradient-to-br from-indigo-500 to-purple-600 flex-shrink-0"></div>
  60. )}
  61. <div className={`max-w-xs md:max-w-md lg:max-w-lg px-4 py-2 rounded-2xl ${msg.sender === 'user' ? 'bg-blue-600 text-white rounded-br-none' : 'bg-slate-200 text-slate-800 rounded-bl-none'}`}>
  62. <p className="text-sm leading-relaxed">{msg.text}</p>
  63. </div>
  64. </div>
  65. ))}
  66. {isTyping && (
  67. <div className="flex items-end gap-2 justify-start">
  68. <div className="w-8 h-8 rounded-full bg-gradient-to-br from-indigo-500 to-purple-600 flex-shrink-0"></div>
  69. <div className="px-4 py-2 rounded-2xl bg-slate-200 rounded-bl-none">
  70. <div className="flex items-center justify-center space-x-1">
  71. <span className="w-1.5 h-1.5 bg-slate-500 rounded-full animate-bounce [animation-delay:-0.3s]"></span>
  72. <span className="w-1.5 h-1.5 bg-slate-500 rounded-full animate-bounce [animation-delay:-0.15s]"></span>
  73. <span className="w-1.5 h-1.5 bg-slate-500 rounded-full animate-bounce"></span>
  74. </div>
  75. </div>
  76. </div>
  77. )}
  78. <div ref={messagesEndRef} />
  79. </main>
  80. {/* Input Form */}
  81. <footer className="p-4 border-t border-slate-200 bg-slate-50 flex-shrink-0">
  82. <form onSubmit={handleSendMessage} className="flex items-center gap-3">
  83. <input
  84. type="text"
  85. value={inputValue}
  86. onChange={e => setInputValue(e.target.value)}
  87. placeholder="Talk to your clone..."
  88. className="flex-1 w-full px-4 py-2 bg-white border border-slate-300 rounded-full focus:outline-none focus:ring-2 focus:ring-blue-500"
  89. autoComplete="off"
  90. />
  91. <button
  92. type="submit"
  93. aria-label="Send message"
  94. className="w-10 h-10 flex-shrink-0 bg-blue-600 text-white rounded-full flex items-center justify-center hover:bg-blue-700 disabled:bg-blue-300 transition-colors"
  95. disabled={inputValue.trim() === ''}
  96. >
  97. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="w-5 h-5">
  98. <path d="M3.105 2.289a.75.75 0 00-.826.95l1.414 4.925A1.5 1.5 0 005.135 9.25h6.115a.75.75 0 010 1.5H5.135a1.5 1.5 0 00-1.442 1.086L2.279 16.76a.75.75 0 00.826.95l14.433-6.414a.75.75 0 000-1.392L3.105 2.289z" />
  99. </svg>
  100. </button>
  101. </form>
  102. </footer>
  103. </div>
  104. );
  105. };
  106. export default CloneChatPage;