diff --git a/frontend/__tests__/components/chat/chat-interface.test.tsx b/frontend/__tests__/components/chat/chat-interface.test.tsx index fc4c03e3f68c..f4e1915c4960 100644 --- a/frontend/__tests__/components/chat/chat-interface.test.tsx +++ b/frontend/__tests__/components/chat/chat-interface.test.tsx @@ -21,6 +21,11 @@ describe("Empty state", () => { })); beforeAll(() => { + vi.mock("@remix-run/react", async (importActual) => ({ + ...(await importActual()), + useRouteLoaderData: vi.fn(() => ({})), + })); + vi.mock("#/context/socket", async (importActual) => ({ ...(await importActual()), useWsClient: useWsClientMock, diff --git a/frontend/src/components/chat-interface.tsx b/frontend/src/components/chat-interface.tsx index 361ea744cade..f0004bd749d4 100644 --- a/frontend/src/components/chat-interface.tsx +++ b/frontend/src/components/chat-interface.tsx @@ -1,6 +1,7 @@ import { useDispatch, useSelector } from "react-redux"; import React from "react"; import posthog from "posthog-js"; +import { useRouteLoaderData } from "@remix-run/react"; import { convertImageToBase64 } from "#/utils/convert-image-to-base-64"; import { ChatMessage } from "./chat-message"; import { FeedbackActions } from "./feedback-actions"; @@ -26,6 +27,9 @@ import { WsClientProviderStatus, } from "#/context/ws-client-provider"; import OpenHands from "#/api/open-hands"; +import { clientLoader } from "#/routes/_oh"; +import { downloadWorkspace } from "#/utils/download-workspace"; +import { SuggestionItem } from "./suggestion-item"; const isErrorMessage = ( message: Message | ErrorMessage, @@ -38,6 +42,7 @@ export function ChatInterface() { const scrollRef = React.useRef(null); const { scrollDomToBottom, onChatBodyScroll, hitBottom } = useScrollToBottom(scrollRef); + const rootLoaderData = useRouteLoaderData("routes/_oh"); const { messages } = useSelector((state: RootState) => state.chat); const { curAgentState } = useSelector((state: RootState) => state.agent); @@ -47,6 +52,7 @@ export function ChatInterface() { >("positive"); const [feedbackModalIsOpen, setFeedbackModalIsOpen] = React.useState(false); const [messageToSend, setMessageToSend] = React.useState(null); + const [isDownloading, setIsDownloading] = React.useState(false); React.useEffect(() => { if (status === WsClientProviderStatus.ACTIVE) { @@ -94,6 +100,17 @@ export function ChatInterface() { setFeedbackPolarity(polarity); }; + const handleDownloadWorkspace = async () => { + setIsDownloading(true); + try { + await downloadWorkspace(); + } catch (error) { + // TODO: Handle error + } finally { + setIsDownloading(false); + } + }; + return (
{messages.length === 0 && ( @@ -128,6 +145,7 @@ export function ChatInterface() {
)} + {!isLoadingMessages && messages.map((message, index) => isErrorMessage(message) ? ( @@ -153,6 +171,34 @@ export function ChatInterface() { ), )} + + {(curAgentState === AgentState.AWAITING_USER_INPUT || + curAgentState === AgentState.FINISHED) && ( +
+ {rootLoaderData?.ghToken ? ( + { + handleSendMessage(value, []); + }} + /> + ) : ( + + )} +
+ )}
diff --git a/frontend/src/components/suggestion-item.tsx b/frontend/src/components/suggestion-item.tsx index 0631d582be4b..cb9b7fdcae6c 100644 --- a/frontend/src/components/suggestion-item.tsx +++ b/frontend/src/components/suggestion-item.tsx @@ -7,12 +7,12 @@ interface SuggestionItemProps { export function SuggestionItem({ suggestion, onClick }: SuggestionItemProps) { return ( -
  • +