Download src/components/message_bar.tsx from IOE-IFAD/test: direct link, hf CLI and curl.
- Browser
- Download file 4.34 kB
-
https://huggingface.co/spaces/IOE-IFAD/test/resolve/main/src/components/message_bar.tsx
- Command line
-
hf download hf://spaces/IOE-IFAD/test/src/components/message_bar.tsx
-
curl -L -o message_bar.tsx https://huggingface.co/spaces/IOE-IFAD/test/resolve/main/src/components/message_bar.tsx
4.34 kB
| "use client"; | |
| import React, { useCallback, useEffect, useState, useRef } from "react"; | |
| import { Textarea } from "@/components/ui/textarea-chat"; | |
| import { Send, Loader2, Sparkles } from "lucide-react"; | |
| import { Button } from "@/components/ui/button"; | |
| import { useChatLogic } from "@/hooks/useChatLogic"; | |
| import type { Chat } from "@/lib/types"; | |
| type Props = { | |
| selectedChat: Chat | null; | |
| selectedChatId: string | null; | |
| scrollToBottom: () => void; | |
| }; | |
| const MessageBar: React.FC<Props> = ({ | |
| selectedChat, | |
| selectedChatId, | |
| scrollToBottom, | |
| }) => { | |
| const { isLoading, handleSubmit } = useChatLogic( | |
| selectedChat, | |
| selectedChatId, | |
| scrollToBottom | |
| ); | |
| const [input, setInput] = useState<string>(""); | |
| const textareaRef = useRef<HTMLTextAreaElement>(null); | |
| // Auto-resize function | |
| const adjustTextareaHeight = useCallback(() => { | |
| const textarea = textareaRef.current; | |
| if (textarea) { | |
| textarea.style.height = "auto"; | |
| const newHeight = Math.min(textarea.scrollHeight, 120); // Reduced max height for mobile | |
| textarea.style.height = `${newHeight}px`; | |
| } | |
| }, []); | |
| useEffect(() => { | |
| adjustTextareaHeight(); | |
| }, [input, adjustTextareaHeight]); | |
| const handleFormSubmit = useCallback( | |
| async (e: React.FormEvent<HTMLFormElement>) => { | |
| e.preventDefault(); | |
| if (input.trim()) { | |
| await handleSubmit(input); | |
| setInput(""); | |
| setTimeout(() => adjustTextareaHeight(), 0); | |
| } | |
| }, | |
| [handleSubmit, input, adjustTextareaHeight] | |
| ); | |
| const handleKeyDown = useCallback( | |
| async (e: React.KeyboardEvent<HTMLTextAreaElement>) => { | |
| if (e.key === "Enter" && !e.shiftKey) { | |
| e.preventDefault(); | |
| if (input.trim()) { | |
| await handleSubmit(input); | |
| setInput(""); | |
| setTimeout(() => adjustTextareaHeight(), 0); | |
| } | |
| } | |
| }, | |
| [handleSubmit, input, adjustTextareaHeight] | |
| ); | |
| useEffect(() => { | |
| const handleKeyPress = (e: KeyboardEvent) => { | |
| if (e.key === "/" && document.activeElement?.tagName !== "TEXTAREA") { | |
| e.preventDefault(); | |
| textareaRef.current?.focus(); | |
| } | |
| }; | |
| window.addEventListener("keydown", handleKeyPress); | |
| return () => window.removeEventListener("keydown", handleKeyPress); | |
| }, []); | |
| return ( | |
| <div className="p-2 max-w-4xl mx-auto w-full"> | |
| <form | |
| onSubmit={handleFormSubmit} | |
| className="flex items-end gap-2 sm:gap-3 bg-gradient-to-r from-background to-muted/20 border border-border/50 rounded-xl sm:rounded-2xl p-2 sm:p-3 shadow-lg backdrop-blur-sm" | |
| > | |
| <div className="flex-1 relative min-w-0"> | |
| <Textarea | |
| ref={textareaRef} | |
| value={input} | |
| onChange={(e) => setInput(e.target.value)} | |
| onKeyDown={handleKeyDown} | |
| placeholder="Ask about your transcripts..." | |
| className="resize-none border-none bg-transparent focus:ring-0 focus:outline-none overflow-y-auto placeholder:text-muted-foreground/60 text-sm sm:text-base" | |
| style={{ minHeight: "36px", maxHeight: "120px" }} | |
| disabled={isLoading} | |
| aria-label="Message input" | |
| /> | |
| {!input && ( | |
| <div className="absolute top-2 sm:top-3 right-2 sm:right-3 text-muted-foreground/40 pointer-events-none"> | |
| <Sparkles className="h-3.5 w-3.5 sm:h-4 sm:w-4" /> | |
| </div> | |
| )} | |
| </div> | |
| <Button | |
| type="submit" | |
| size="icon" | |
| className="h-9 w-9 sm:h-10 sm:w-10 rounded-lg sm:rounded-xl bg-gradient-to-r from-primary to-primary/80 hover:from-primary/90 hover:to-primary/70 transition-all duration-200 shadow-md hover:shadow-lg hover:scale-105 flex-shrink-0" | |
| disabled={isLoading || selectedChatId === null || !input.trim()} | |
| aria-label="Send message" | |
| > | |
| {isLoading ? ( | |
| <Loader2 className="h-4 w-4 animate-spin" /> | |
| ) : ( | |
| <Send className="h-4 w-4" /> | |
| )} | |
| </Button> | |
| </form> | |
| <div className="text-center mt-2 hidden sm:block"> | |
| <p className="text-xs text-muted-foreground/60"> | |
| Answers are generated by AI and may not be accurate. Use at your own | |
| discretion. | |
| </p> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| export default React.memo(MessageBar); | |