From c13b47cd50f455fd71280eea2cfd72e61118c0d5 Mon Sep 17 00:00:00 2001 From: Vaishakh-SM Date: Tue, 26 Nov 2024 22:40:09 +0530 Subject: [PATCH 01/11] Adding translations for the Agent Control Bar --- frontend/src/components/agent-control-bar.tsx | 8 +++++-- frontend/src/i18n/translation.json | 24 ++++++++++++------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/agent-control-bar.tsx b/frontend/src/components/agent-control-bar.tsx index a043f2a6a0a8..573944fd578f 100644 --- a/frontend/src/components/agent-control-bar.tsx +++ b/frontend/src/components/agent-control-bar.tsx @@ -1,12 +1,14 @@ import { Tooltip } from "@nextui-org/react"; import React from "react"; import { useSelector } from "react-redux"; +import { useTranslation } from "react-i18next"; import PauseIcon from "#/assets/pause"; import PlayIcon from "#/assets/play"; import { generateAgentStateChangeEvent } from "#/services/agent-state-service"; import { RootState } from "#/store"; import AgentState from "#/types/agent-state"; import { useWsClient } from "#/context/ws-client-provider"; +import { I18nKey } from "#/i18n/declaration"; const IgnoreTaskStateMap: Record = { [AgentState.PAUSED]: [ @@ -81,6 +83,8 @@ function AgentControlBar() { } }; + const { t } = useTranslation(); + return (
Date: Tue, 26 Nov 2024 22:42:50 +0530 Subject: [PATCH 02/11] Adding translation for Alt Text in the Browser Panel --- frontend/src/components/browser.tsx | 2 +- frontend/src/i18n/translation.json | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/browser.tsx b/frontend/src/components/browser.tsx index 6541ff98ff9f..f22860ec8ca3 100644 --- a/frontend/src/components/browser.tsx +++ b/frontend/src/components/browser.tsx @@ -27,7 +27,7 @@ function BrowserPanel() { src={imgSrc} style={{ objectFit: "contain", width: "100%", height: "auto" }} className="rounded-xl" - alt="Browser Screenshot" + alt={t(I18nKey.BROWSER$SCREENSHOT_ALT)} /> ) : (
diff --git a/frontend/src/i18n/translation.json b/frontend/src/i18n/translation.json index 8c07ea6df915..c3ce81f6ca18 100644 --- a/frontend/src/i18n/translation.json +++ b/frontend/src/i18n/translation.json @@ -2018,5 +2018,8 @@ }, "ACTION_BUTTON$PAUSE": { "en": "Pause the current task" + }, + "BROWSER$SCREENSHOT_ALT": { + "en": "Browser Screenshot" } } From ee878bcf825602fbfeed979467aa4d3a251098b5 Mon Sep 17 00:00:00 2001 From: Vaishakh-SM Date: Tue, 26 Nov 2024 22:46:25 +0530 Subject: [PATCH 03/11] Adding Translations to the Account Settings Context Menu --- .../context-menu/account-settings-context-menu.tsx | 7 +++++-- frontend/src/i18n/translation.json | 6 ++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/context-menu/account-settings-context-menu.tsx b/frontend/src/components/context-menu/account-settings-context-menu.tsx index 960eb2d7a8c6..bf695cbc6ba7 100644 --- a/frontend/src/components/context-menu/account-settings-context-menu.tsx +++ b/frontend/src/components/context-menu/account-settings-context-menu.tsx @@ -1,7 +1,9 @@ +import { useTranslation } from "react-i18next"; import { ContextMenu } from "./context-menu"; import { ContextMenuListItem } from "./context-menu-list-item"; import { ContextMenuSeparator } from "./context-menu-separator"; import { useClickOutsideElement } from "#/hooks/use-click-outside-element"; +import { I18nKey } from "#/i18n/declaration"; interface AccountSettingsContextMenuProps { onClickAccountSettings: () => void; @@ -17,6 +19,7 @@ export function AccountSettingsContextMenu({ isLoggedIn, }: AccountSettingsContextMenuProps) { const ref = useClickOutsideElement(onClose); + const { t } = useTranslation(); return ( - Account Settings + {t(I18nKey.ACCOUNT_SETTINGS$SETTINGS)} - Logout + {t(I18nKey.ACCOUNT_SETTINGS$LOGOUT)} ); diff --git a/frontend/src/i18n/translation.json b/frontend/src/i18n/translation.json index c3ce81f6ca18..946402bdaa21 100644 --- a/frontend/src/i18n/translation.json +++ b/frontend/src/i18n/translation.json @@ -2021,5 +2021,11 @@ }, "BROWSER$SCREENSHOT_ALT": { "en": "Browser Screenshot" + }, + "ACCOUNT_SETTINGS$SETTINGS": { + "en": "Account Settings" + }, + "ACCOUNT_SETTINGS$LOGOUT": { + "en": "Logout" } } From 852a69d03a4f78ecf3b1b07679063dce13ffcd99 Mon Sep 17 00:00:00 2001 From: Vaishakh-SM Date: Tue, 26 Nov 2024 22:53:14 +0530 Subject: [PATCH 04/11] Adding translation for the Error toast component --- frontend/src/components/error-toast.tsx | 6 +++++- frontend/src/i18n/translation.json | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/error-toast.tsx b/frontend/src/components/error-toast.tsx index 30f2d7cbe6b5..36144eff9651 100644 --- a/frontend/src/components/error-toast.tsx +++ b/frontend/src/components/error-toast.tsx @@ -1,4 +1,6 @@ import toast, { Toast } from "react-hot-toast"; +import { useTranslation } from "react-i18next"; +import { I18nKey } from "#/i18n/declaration"; interface ErrorToastProps { id: Toast["id"]; @@ -6,6 +8,8 @@ interface ErrorToastProps { } export function ErrorToast({ id, error }: ErrorToastProps) { + const { t } = useTranslation(); + return (
{error} @@ -14,7 +18,7 @@ export function ErrorToast({ id, error }: ErrorToastProps) { onClick={() => toast.dismiss(id)} className="bg-neutral-500 px-1 rounded h-full" > - Close + {t(I18nKey.ERROR_TOAST$CLOSE_BUTTON_LABEL)}
); diff --git a/frontend/src/i18n/translation.json b/frontend/src/i18n/translation.json index 946402bdaa21..522078364978 100644 --- a/frontend/src/i18n/translation.json +++ b/frontend/src/i18n/translation.json @@ -2027,5 +2027,8 @@ }, "ACCOUNT_SETTINGS$LOGOUT": { "en": "Logout" + }, + "ERROR_TOAST$CLOSE_BUTTON_LABEL": { + "en": "Close" } } From 74235144a0e96456aa6c8bec534c64832f2e5390 Mon Sep 17 00:00:00 2001 From: Vaishakh-SM Date: Tue, 26 Nov 2024 23:01:02 +0530 Subject: [PATCH 05/11] Adding translations for the file-explorer file --- .../src/components/file-explorer/file-explorer.tsx | 12 +++++++++--- frontend/src/i18n/translation.json | 12 ++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/file-explorer/file-explorer.tsx b/frontend/src/components/file-explorer/file-explorer.tsx index 5c82a7fa8482..a81bd9d910da 100644 --- a/frontend/src/components/file-explorer/file-explorer.tsx +++ b/frontend/src/components/file-explorer/file-explorer.tsx @@ -35,6 +35,8 @@ function ExplorerActions({ onUpload, isHidden, }: ExplorerActionsProps) { + const { t } = useTranslation(); + return (
} testId="refresh" - ariaLabel="Refresh workspace" + ariaLabel={t(I18nKey.FILE_EXPLORER$REFRESH_WORKSPACE)} onClick={onRefresh} /> } testId="upload" - ariaLabel="Upload File" + ariaLabel={t(I18nKey.FILE_EXPLORER$UPLOAD)} onClick={onUpload} /> @@ -84,7 +86,11 @@ function ExplorerActions({ ) } testId="toggle" - ariaLabel={isHidden ? "Open workspace" : "Close workspace"} + ariaLabel={ + isHidden + ? t(I18nKey.FILE_EXPLORER$OPEN_WORKSPACE) + : t(I18nKey.FILE_EXPLORER$CLOSE_WORKSPACE) + } onClick={toggleHidden} />
diff --git a/frontend/src/i18n/translation.json b/frontend/src/i18n/translation.json index 522078364978..de9119031431 100644 --- a/frontend/src/i18n/translation.json +++ b/frontend/src/i18n/translation.json @@ -2030,5 +2030,17 @@ }, "ERROR_TOAST$CLOSE_BUTTON_LABEL": { "en": "Close" + }, + "FILE_EXPLORER$UPLOAD": { + "en": "Upload File" + }, + "FILE_EXPLORER$REFRESH_WORKSPACE": { + "en": "Refresh workspace" + }, + "FILE_EXPLORER$OPEN_WORKSPACE": { + "en": "Open workspace" + }, + "FILE_EXPLORER$CLOSE_WORKSPACE": { + "en": "Close workspace" } } From 184c00248848f1ae252b51737ce4113e54bb8724 Mon Sep 17 00:00:00 2001 From: Vaishakh-SM Date: Tue, 26 Nov 2024 23:04:51 +0530 Subject: [PATCH 06/11] Add translation for Aria Label in ProjectMenuCard --- frontend/src/components/project-menu/ProjectMenuCard.tsx | 5 ++++- frontend/src/i18n/translation.json | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/project-menu/ProjectMenuCard.tsx b/frontend/src/components/project-menu/ProjectMenuCard.tsx index e61ec954c059..683fa134a2ca 100644 --- a/frontend/src/components/project-menu/ProjectMenuCard.tsx +++ b/frontend/src/components/project-menu/ProjectMenuCard.tsx @@ -2,6 +2,7 @@ import React from "react"; import { useDispatch } from "react-redux"; import toast from "react-hot-toast"; import posthog from "posthog-js"; +import { useTranslation } from "react-i18next"; import EllipsisH from "#/icons/ellipsis-h.svg?react"; import { ModalBackdrop } from "../modals/modal-backdrop"; import { ConnectToGitHubModal } from "../modals/connect-to-github-modal"; @@ -13,6 +14,7 @@ import { ProjectMenuDetails } from "./project-menu-details"; import { downloadWorkspace } from "#/utils/download-workspace"; import { LoadingSpinner } from "../modals/loading-project"; import { useWsClient } from "#/context/ws-client-provider"; +import { I18nKey } from "#/i18n/declaration"; interface ProjectMenuCardProps { isConnectedToGitHub: boolean; @@ -29,6 +31,7 @@ export function ProjectMenuCard({ }: ProjectMenuCardProps) { const { send } = useWsClient(); const dispatch = useDispatch(); + const { t } = useTranslation(); const [contextMenuIsOpen, setContextMenuIsOpen] = React.useState(false); const [connectToGitHubModalOpen, setConnectToGitHubModalOpen] = @@ -99,7 +102,7 @@ Please push the changes to GitHub and open a pull request. diff --git a/frontend/src/i18n/translation.json b/frontend/src/i18n/translation.json index ab456e069915..746349c04dc8 100644 --- a/frontend/src/i18n/translation.json +++ b/frontend/src/i18n/translation.json @@ -1984,6 +1984,12 @@ "en": "New Project", "es": "Nuevo proyecto" }, + "PROJECT_MENU_DETAILS_PLACEHOLDER$CONNECT_TO_GITHUB": { + "en": "Connect to GitHub" + }, + "PROJECT_MENU_DETAILS_PLACEHOLDER$CONNECTED": { + "en": "Connected" + }, "PROJECT_MENU_DETAILS$AGO_LABEL": { "en": "ago", "es": "atrás" From 63218470e3dfd8465987fc8a1201d941eff347eb Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 11 Dec 2024 09:39:50 +0000 Subject: [PATCH 08/11] Fix tests: Update text assertions to use translation keys --- .../account-settings-context-menu.test.tsx | 12 ++++++------ frontend/__tests__/components/user-actions.test.tsx | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/frontend/__tests__/components/context-menu/account-settings-context-menu.test.tsx b/frontend/__tests__/components/context-menu/account-settings-context-menu.test.tsx index ad5b6a0a3443..89780e07aef7 100644 --- a/frontend/__tests__/components/context-menu/account-settings-context-menu.test.tsx +++ b/frontend/__tests__/components/context-menu/account-settings-context-menu.test.tsx @@ -28,8 +28,8 @@ describe("AccountSettingsContextMenu", () => { expect( screen.getByTestId("account-settings-context-menu"), ).toBeInTheDocument(); - expect(screen.getByText("Account Settings")).toBeInTheDocument(); - expect(screen.getByText("Logout")).toBeInTheDocument(); + expect(screen.getByText("ACCOUNT_SETTINGS$SETTINGS")).toBeInTheDocument(); + expect(screen.getByText("ACCOUNT_SETTINGS$LOGOUT")).toBeInTheDocument(); }); it("should call onClickAccountSettings when the account settings option is clicked", async () => { @@ -42,7 +42,7 @@ describe("AccountSettingsContextMenu", () => { />, ); - const accountSettingsOption = screen.getByText("Account Settings"); + const accountSettingsOption = screen.getByText("ACCOUNT_SETTINGS$SETTINGS"); await user.click(accountSettingsOption); expect(onClickAccountSettingsMock).toHaveBeenCalledOnce(); @@ -58,7 +58,7 @@ describe("AccountSettingsContextMenu", () => { />, ); - const logoutOption = screen.getByText("Logout"); + const logoutOption = screen.getByText("ACCOUNT_SETTINGS$LOGOUT"); await user.click(logoutOption); expect(onLogoutMock).toHaveBeenCalledOnce(); @@ -74,7 +74,7 @@ describe("AccountSettingsContextMenu", () => { />, ); - const logoutOption = screen.getByText("Logout"); + const logoutOption = screen.getByText("ACCOUNT_SETTINGS$LOGOUT"); await user.click(logoutOption); expect(onLogoutMock).not.toHaveBeenCalled(); @@ -90,7 +90,7 @@ describe("AccountSettingsContextMenu", () => { />, ); - const accountSettingsButton = screen.getByText("Account Settings"); + const accountSettingsButton = screen.getByText("ACCOUNT_SETTINGS$SETTINGS"); await user.click(accountSettingsButton); await user.click(document.body); diff --git a/frontend/__tests__/components/user-actions.test.tsx b/frontend/__tests__/components/user-actions.test.tsx index a83b88a38923..143af7d7113f 100644 --- a/frontend/__tests__/components/user-actions.test.tsx +++ b/frontend/__tests__/components/user-actions.test.tsx @@ -58,7 +58,7 @@ describe("UserActions", () => { const userAvatar = screen.getByTestId("user-avatar"); await user.click(userAvatar); - const accountSettingsOption = screen.getByText("Account Settings"); + const accountSettingsOption = screen.getByText("ACCOUNT_SETTINGS$SETTINGS"); await user.click(accountSettingsOption); expect(onClickAccountSettingsMock).toHaveBeenCalledOnce(); @@ -79,7 +79,7 @@ describe("UserActions", () => { const userAvatar = screen.getByTestId("user-avatar"); await user.click(userAvatar); - const logoutOption = screen.getByText("Logout"); + const logoutOption = screen.getByText("ACCOUNT_SETTINGS$LOGOUT"); await user.click(logoutOption); expect(onLogoutMock).toHaveBeenCalledOnce(); @@ -99,7 +99,7 @@ describe("UserActions", () => { const userAvatar = screen.getByTestId("user-avatar"); await user.click(userAvatar); - const logoutOption = screen.getByText("Logout"); + const logoutOption = screen.getByText("ACCOUNT_SETTINGS$LOGOUT"); await user.click(logoutOption); expect(onLogoutMock).not.toHaveBeenCalled(); From 576c5e5ea3dfe7e81d636cc618563096a9c72df3 Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 11 Dec 2024 15:41:35 +0000 Subject: [PATCH 09/11] Fix imports and move file-explorer to match main branch structure --- frontend/src/components/file-explorer/file-explorer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/file-explorer/file-explorer.tsx b/frontend/src/components/file-explorer/file-explorer.tsx index a81bd9d910da..fc3116025db5 100644 --- a/frontend/src/components/file-explorer/file-explorer.tsx +++ b/frontend/src/components/file-explorer/file-explorer.tsx @@ -11,8 +11,8 @@ import { useTranslation } from "react-i18next"; import { twMerge } from "tailwind-merge"; import AgentState from "#/types/agent-state"; import { addAssistantMessage } from "#/state/chat-slice"; -import IconButton from "../icon-button"; -import ExplorerTree from "./explorer-tree"; +import IconButton from "#/components/shared/buttons/icon-button"; +import ExplorerTree from "#/components/features/file-explorer/explorer-tree"; import toast from "#/utils/toast"; import { RootState } from "#/store"; import { I18nKey } from "#/i18n/declaration"; From 347a3702027c4a7baf886c86952756562d70f198 Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 11 Dec 2024 16:06:39 +0000 Subject: [PATCH 10/11] Fix imports: Use named imports for IconButton and ExplorerTree --- frontend/src/components/file-explorer/file-explorer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/file-explorer/file-explorer.tsx b/frontend/src/components/file-explorer/file-explorer.tsx index fc3116025db5..d9b648cfd124 100644 --- a/frontend/src/components/file-explorer/file-explorer.tsx +++ b/frontend/src/components/file-explorer/file-explorer.tsx @@ -11,8 +11,8 @@ import { useTranslation } from "react-i18next"; import { twMerge } from "tailwind-merge"; import AgentState from "#/types/agent-state"; import { addAssistantMessage } from "#/state/chat-slice"; -import IconButton from "#/components/shared/buttons/icon-button"; -import ExplorerTree from "#/components/features/file-explorer/explorer-tree"; +import { IconButton } from "#/components/shared/buttons/icon-button"; +import { ExplorerTree } from "#/components/features/file-explorer/explorer-tree"; import toast from "#/utils/toast"; import { RootState } from "#/store"; import { I18nKey } from "#/i18n/declaration"; From cdf5ce3e233d160b4a171f4659a29d6608498695 Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 11 Dec 2024 16:23:07 +0000 Subject: [PATCH 11/11] Remove unrelated files: agent-control-bar.tsx and file-explorer.tsx --- frontend/src/components/agent-control-bar.tsx | 114 ------- .../file-explorer/file-explorer.tsx | 313 ------------------ 2 files changed, 427 deletions(-) delete mode 100644 frontend/src/components/agent-control-bar.tsx delete mode 100644 frontend/src/components/file-explorer/file-explorer.tsx diff --git a/frontend/src/components/agent-control-bar.tsx b/frontend/src/components/agent-control-bar.tsx deleted file mode 100644 index 573944fd578f..000000000000 --- a/frontend/src/components/agent-control-bar.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { Tooltip } from "@nextui-org/react"; -import React from "react"; -import { useSelector } from "react-redux"; -import { useTranslation } from "react-i18next"; -import PauseIcon from "#/assets/pause"; -import PlayIcon from "#/assets/play"; -import { generateAgentStateChangeEvent } from "#/services/agent-state-service"; -import { RootState } from "#/store"; -import AgentState from "#/types/agent-state"; -import { useWsClient } from "#/context/ws-client-provider"; -import { I18nKey } from "#/i18n/declaration"; - -const IgnoreTaskStateMap: Record = { - [AgentState.PAUSED]: [ - AgentState.INIT, - AgentState.PAUSED, - AgentState.STOPPED, - AgentState.FINISHED, - AgentState.REJECTED, - AgentState.AWAITING_USER_INPUT, - AgentState.AWAITING_USER_CONFIRMATION, - ], - [AgentState.RUNNING]: [ - AgentState.INIT, - AgentState.RUNNING, - AgentState.STOPPED, - AgentState.FINISHED, - AgentState.REJECTED, - AgentState.AWAITING_USER_INPUT, - AgentState.AWAITING_USER_CONFIRMATION, - ], - [AgentState.STOPPED]: [AgentState.INIT, AgentState.STOPPED], - [AgentState.USER_CONFIRMED]: [AgentState.RUNNING], - [AgentState.USER_REJECTED]: [AgentState.RUNNING], - [AgentState.AWAITING_USER_CONFIRMATION]: [], -}; - -interface ActionButtonProps { - isDisabled?: boolean; - content: string; - action: AgentState; - handleAction: (action: AgentState) => void; - large?: boolean; -} - -function ActionButton({ - isDisabled = false, - content, - action, - handleAction, - children, - large = false, -}: React.PropsWithChildren) { - return ( - - - - ); -} - -function AgentControlBar() { - const { send } = useWsClient(); - const { curAgentState } = useSelector((state: RootState) => state.agent); - - const handleAction = (action: AgentState) => { - if (!IgnoreTaskStateMap[action].includes(curAgentState)) { - send(generateAgentStateChangeEvent(action)); - } - }; - - const { t } = useTranslation(); - - return ( -
- - {curAgentState === AgentState.PAUSED ? : } - -
- ); -} - -export default AgentControlBar; diff --git a/frontend/src/components/file-explorer/file-explorer.tsx b/frontend/src/components/file-explorer/file-explorer.tsx deleted file mode 100644 index d9b648cfd124..000000000000 --- a/frontend/src/components/file-explorer/file-explorer.tsx +++ /dev/null @@ -1,313 +0,0 @@ -import React from "react"; -import { - IoIosArrowBack, - IoIosArrowForward, - IoIosRefresh, - IoIosCloudUpload, -} from "react-icons/io"; -import { useDispatch, useSelector } from "react-redux"; -import { IoFileTray } from "react-icons/io5"; -import { useTranslation } from "react-i18next"; -import { twMerge } from "tailwind-merge"; -import AgentState from "#/types/agent-state"; -import { addAssistantMessage } from "#/state/chat-slice"; -import { IconButton } from "#/components/shared/buttons/icon-button"; -import { ExplorerTree } from "#/components/features/file-explorer/explorer-tree"; -import toast from "#/utils/toast"; -import { RootState } from "#/store"; -import { I18nKey } from "#/i18n/declaration"; -import OpenHands from "#/api/open-hands"; -import VSCodeIcon from "#/assets/vscode-alt.svg?react"; -import { useListFiles } from "#/hooks/query/use-list-files"; -import { FileUploadSuccessResponse } from "#/api/open-hands.types"; -import { useUploadFiles } from "#/hooks/mutation/use-upload-files"; - -interface ExplorerActionsProps { - onRefresh: () => void; - onUpload: () => void; - toggleHidden: () => void; - isHidden: boolean; -} - -function ExplorerActions({ - toggleHidden, - onRefresh, - onUpload, - isHidden, -}: ExplorerActionsProps) { - const { t } = useTranslation(); - - return ( -
- {!isHidden && ( - <> - - } - testId="refresh" - ariaLabel={t(I18nKey.FILE_EXPLORER$REFRESH_WORKSPACE)} - onClick={onRefresh} - /> - - } - testId="upload" - ariaLabel={t(I18nKey.FILE_EXPLORER$UPLOAD)} - onClick={onUpload} - /> - - )} - - - ) : ( - - ) - } - testId="toggle" - ariaLabel={ - isHidden - ? t(I18nKey.FILE_EXPLORER$OPEN_WORKSPACE) - : t(I18nKey.FILE_EXPLORER$CLOSE_WORKSPACE) - } - onClick={toggleHidden} - /> -
- ); -} - -interface FileExplorerProps { - isOpen: boolean; - onToggle: () => void; -} - -function FileExplorer({ isOpen, onToggle }: FileExplorerProps) { - const [isDragging, setIsDragging] = React.useState(false); - - const { curAgentState } = useSelector((state: RootState) => state.agent); - const fileInputRef = React.useRef(null); - const dispatch = useDispatch(); - const { t } = useTranslation(); - const selectFileInput = () => { - fileInputRef.current?.click(); // Trigger the file browser - }; - - const { data: paths, refetch, error } = useListFiles(); - - const handleUploadSuccess = (data: FileUploadSuccessResponse) => { - const uploadedCount = data.uploaded_files.length; - const skippedCount = data.skipped_files.length; - - if (uploadedCount > 0) { - toast.success( - `upload-success-${new Date().getTime()}`, - t(I18nKey.EXPLORER$UPLOAD_SUCCESS_MESSAGE, { - count: uploadedCount, - }), - ); - } - - if (skippedCount > 0) { - const message = t(I18nKey.EXPLORER$UPLOAD_PARTIAL_SUCCESS_MESSAGE, { - count: skippedCount, - }); - toast.info(message); - } - - if (uploadedCount === 0 && skippedCount === 0) { - toast.info(t(I18nKey.EXPLORER$NO_FILES_UPLOADED_MESSAGE)); - } - }; - - const handleUploadError = (e: Error) => { - toast.error( - `upload-error-${new Date().getTime()}`, - e.message || t(I18nKey.EXPLORER$UPLOAD_ERROR_MESSAGE), - ); - }; - - const { mutate: uploadFiles } = useUploadFiles(); - - const refreshWorkspace = () => { - if ( - curAgentState !== AgentState.LOADING && - curAgentState !== AgentState.STOPPED - ) { - refetch(); - } - }; - - const uploadFileData = (files: FileList) => { - uploadFiles( - { files: Array.from(files) }, - { onSuccess: handleUploadSuccess, onError: handleUploadError }, - ); - refreshWorkspace(); - }; - - const handleVSCodeClick = async (e: React.MouseEvent) => { - e.preventDefault(); - try { - const response = await OpenHands.getVSCodeUrl(); - if (response.vscode_url) { - dispatch( - addAssistantMessage( - "You opened VS Code. Please inform the agent of any changes you made to the workspace or environment. To avoid conflicts, it's best to pause the agent before making any changes.", - ), - ); - window.open(response.vscode_url, "_blank"); - } else { - toast.error( - `open-vscode-error-${new Date().getTime()}`, - t(I18nKey.EXPLORER$VSCODE_SWITCHING_ERROR_MESSAGE, { - error: response.error, - }), - ); - } - } catch (exp_error) { - toast.error( - `open-vscode-error-${new Date().getTime()}`, - t(I18nKey.EXPLORER$VSCODE_SWITCHING_ERROR_MESSAGE, { - error: String(exp_error), - }), - ); - } - }; - - React.useEffect(() => { - refreshWorkspace(); - }, [curAgentState]); - - return ( -
{ - setIsDragging(true); - }} - onDragEnd={() => { - setIsDragging(false); - }} - > - {isDragging && ( -
setIsDragging(false)} - onDrop={(event) => { - event.preventDefault(); - const { files: droppedFiles } = event.dataTransfer; - if (droppedFiles.length > 0) { - uploadFileData(droppedFiles); - } - setIsDragging(false); - }} - onDragOver={(event) => event.preventDefault()} - className="z-10 absolute flex flex-col justify-center items-center bg-black top-0 bottom-0 left-0 right-0 opacity-65" - > - -

- {t(I18nKey.EXPLORER$LABEL_DROP_FILES)} -

-
- )} -
-
-
-
- {isOpen && ( -
- {t(I18nKey.EXPLORER$LABEL_WORKSPACE)} -
- )} - -
-
- {!error && ( -
-
- -
-
- )} - {error && ( -
-

{error.message}

-
- )} - {isOpen && ( - - )} -
- { - const { files: selectedFiles } = event.target; - if (selectedFiles && selectedFiles.length > 0) { - uploadFileData(selectedFiles); - } - }} - /> -
-
- ); -} - -export default FileExplorer;