Mercurial
view love/epi/src/components/ChatUI/Composer.tsx @ 73:ede391ac83c8
[MrJuneJune] My own websites.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Wed, 31 Dec 2025 09:07:29 -0800 |
| parents | cf9caa4abc3e |
| children |
line wrap: on
line source
import { useEffect, useRef } from 'react'; import type { ReactNode } from 'react'; import { SendHorizonal, Square } from 'lucide-react'; import { useComposer } from '@/hooks/useComposer'; import { useChat } from '@/hooks/useChat'; import { broadcastDone } from '@/hooks/useChatWebsocket'; import { apiUrl } from '@/utils'; import { currentChatId } from '@/atoms/chatAtoms'; import { useAtom } from 'jotai'; import { useNavigate } from '@tanstack/react-router'; interface ComposerProps { chatId: string; } export function Composer({ chatId }: ComposerProps): ReactNode { const { text, setText, isSending, isDisabled, setSending } = useComposer(); const [_, setCurrentChatId] = useAtom(currentChatId); const { sendUserMessage } = useChat(chatId); const textareaRef = useRef<HTMLTextAreaElement>(null); // Auto-resize useEffect(() => { const el = textareaRef.current; if (el) { el.style.height = 'auto'; el.style.height = `${el.scrollHeight}px`; } }, [text]); const navigate = useNavigate(); const handleSubmit = async () => { console.log('before'); // Stoping the calls if (isSending) { console.log('called'); broadcastDone(chatId); return; } // Don't send if it is diabled or no text if (isDisabled || !text.trim()) return; let newChatId: string = ''; if (!chatId || chatId === 'new') { const res = await fetch(apiUrl('/chats'), { method: 'POST' }); const data = await res.json(); await navigate( { to: '/chat/$chatId', params: { chatId: data.id } }, ); console.log('problem: 1'); setCurrentChatId(data.id); newChatId = data.id; } const message = text.trim(); setSending(); setText(''); sendUserMessage(message, newChatId); }; const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSubmit(); } }; return ( <div className="border-t border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900"> <div className="max-w-4xl mx-auto p-4"> <div className="flex items-end gap-3 bg-gray-50 dark:bg-gray-800/50 rounded-2xl px-4 py-3 shadow-sm ring-1 ring-gray-200 dark:ring-gray-700 focus-within:ring-2 focus-within:ring-blue-500"> <textarea ref={textareaRef} value={text} onChange={(e) => setText(e.target.value)} onKeyDown={handleKeyDown} placeholder="Send a message..." rows={1} className="flex-1 resize-none bg-transparent outline-none text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400 text-base leading-6 overflow-hidden max-h-96" /> <button onClick={handleSubmit} disabled={isDisabled} className={`w-10 h-10 rounded-full flex items-center justify-center transition-all ${ isDisabled ? 'bg-gray-300 dark:bg-gray-700 text-gray-500' : isSending ? 'bg-red-500 hover:bg-red-600 text-white' : 'bg-blue-500 hover:bg-blue-600 text-white' }`} > {isSending ? <Square className="w-5 h-5" /> : <SendHorizonal className="w-5 h-5" />} </button> </div> </div> </div> ); }