|
|
@@ -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<HTMLDivElement>(null);
|
|
|
const { scrollDomToBottom, onChatBodyScroll, hitBottom } =
|
|
|
useScrollToBottom(scrollRef);
|
|
|
+ const rootLoaderData = useRouteLoaderData<typeof clientLoader>("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<string | null>(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 (
|
|
|
<div className="h-full flex flex-col justify-between">
|
|
|
{messages.length === 0 && (
|
|
|
@@ -128,6 +145,7 @@ export function ChatInterface() {
|
|
|
<div className="w-6 h-6 border-2 border-t-[4px] border-primary-500 rounded-full animate-spin" />
|
|
|
</div>
|
|
|
)}
|
|
|
+
|
|
|
{!isLoadingMessages &&
|
|
|
messages.map((message, index) =>
|
|
|
isErrorMessage(message) ? (
|
|
|
@@ -153,6 +171,34 @@ export function ChatInterface() {
|
|
|
</ChatMessage>
|
|
|
),
|
|
|
)}
|
|
|
+
|
|
|
+ {(curAgentState === AgentState.AWAITING_USER_INPUT ||
|
|
|
+ curAgentState === AgentState.FINISHED) && (
|
|
|
+ <div className="flex flex-col gap-2 mb-2">
|
|
|
+ {rootLoaderData?.ghToken ? (
|
|
|
+ <SuggestionItem
|
|
|
+ suggestion={{
|
|
|
+ label: "Push to GitHub",
|
|
|
+ value:
|
|
|
+ "Please push the changes to GitHub and open a pull request.",
|
|
|
+ }}
|
|
|
+ onClick={(value) => {
|
|
|
+ handleSendMessage(value, []);
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ ) : (
|
|
|
+ <SuggestionItem
|
|
|
+ suggestion={{
|
|
|
+ label: !isDownloading
|
|
|
+ ? "Download .zip"
|
|
|
+ : "Downloading, please wait...",
|
|
|
+ value: "Download .zip",
|
|
|
+ }}
|
|
|
+ onClick={handleDownloadWorkspace}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
</div>
|
|
|
|
|
|
<div className="flex flex-col gap-[6px] px-4 pb-4">
|