From 1e31eb43073a6875dd44632a1e381cabaa45c18c Mon Sep 17 00:00:00 2001 From: d-kimsuon Date: Sun, 7 Sep 2025 02:49:00 +0900 Subject: [PATCH] refactor: unify NewChat and ResumeChat Input Component --- .../components/chatForm/ChatInput.tsx | 146 +++++++++++++++ .../CommandCompletion.tsx | 0 .../{newChat => chatForm}/FileCompletion.tsx | 10 +- .../[projectId]/components/chatForm/index.ts | 7 + .../components/chatForm/useChatMutations.ts | 75 ++++++++ .../components/newChat/NewChat.tsx | 166 ++---------------- .../components/resumeChat/ResumeChat.tsx | 154 +++------------- 7 files changed, 271 insertions(+), 287 deletions(-) create mode 100644 src/app/projects/[projectId]/components/chatForm/ChatInput.tsx rename src/app/projects/[projectId]/components/{newChat => chatForm}/CommandCompletion.tsx (100%) rename src/app/projects/[projectId]/components/{newChat => chatForm}/FileCompletion.tsx (96%) create mode 100644 src/app/projects/[projectId]/components/chatForm/index.ts create mode 100644 src/app/projects/[projectId]/components/chatForm/useChatMutations.ts diff --git a/src/app/projects/[projectId]/components/chatForm/ChatInput.tsx b/src/app/projects/[projectId]/components/chatForm/ChatInput.tsx new file mode 100644 index 0000000..3e8c37c --- /dev/null +++ b/src/app/projects/[projectId]/components/chatForm/ChatInput.tsx @@ -0,0 +1,146 @@ +import { AlertCircleIcon, LoaderIcon, SendIcon } from "lucide-react"; +import { type FC, useId, useRef, useState } from "react"; +import { Button } from "../../../../../components/ui/button"; +import { Textarea } from "../../../../../components/ui/textarea"; +import { + CommandCompletion, + type CommandCompletionRef, +} from "./CommandCompletion"; +import { FileCompletion, type FileCompletionRef } from "./FileCompletion"; + +export interface ChatInputProps { + projectId: string; + onSubmit: (message: string) => void; + isPending: boolean; + error?: Error | null; + placeholder: string; + buttonText: string; + minHeight?: string; + containerClassName?: string; + disabled?: boolean; + buttonSize?: "sm" | "default" | "lg"; +} + +export const ChatInput: FC = ({ + projectId, + onSubmit, + isPending, + error, + placeholder, + buttonText, + minHeight = "min-h-[100px]", + containerClassName = "", + disabled = false, + buttonSize = "lg", +}) => { + const textareaRef = useRef(null); + const [message, setMessage] = useState(""); + const commandCompletionRef = useRef(null); + const fileCompletionRef = useRef(null); + const helpId = useId(); + + const handleSubmit = () => { + if (!message.trim()) return; + onSubmit(message.trim()); + setMessage(""); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (fileCompletionRef.current?.handleKeyDown(e)) { + return; + } + + if (commandCompletionRef.current?.handleKeyDown(e)) { + return; + } + + if (e.key === "Enter" && e.shiftKey) { + e.preventDefault(); + handleSubmit(); + } + }; + + const handleCommandSelect = (command: string) => { + setMessage(command); + textareaRef.current?.focus(); + }; + + const handleFileSelect = (filePath: string) => { + setMessage(filePath); + textareaRef.current?.focus(); + }; + + return ( +
+ {error && ( +
+ + Failed to send message. Please try again. +
+ )} + +
+
+