diff --git a/frontend/src/components/features/file-explorer/tree-node.tsx b/frontend/src/components/features/file-explorer/tree-node.tsx
index 1410e38a9c..53a8fb85d1 100644
--- a/frontend/src/components/features/file-explorer/tree-node.tsx
+++ b/frontend/src/components/features/file-explorer/tree-node.tsx
@@ -14,13 +14,7 @@ interface TreeNodeProps {
}
function TreeNode({ path, defaultOpen = false }: TreeNodeProps) {
- const {
- setFileContent,
- modifiedFiles,
- setSelectedPath,
- files,
- selectedPath,
- } = useFiles();
+ const { setFileContent, setSelectedPath, files, selectedPath } = useFiles();
const [isOpen, setIsOpen] = React.useState(defaultOpen);
const { curAgentState } = useSelector((state: RootState) => state.agent);
@@ -35,8 +29,7 @@ function TreeNode({ path, defaultOpen = false }: TreeNodeProps) {
React.useEffect(() => {
if (fileContent) {
- const code = modifiedFiles[path] || files[path];
- if (!code || fileContent !== files[path]) {
+ if (fileContent !== files[path]) {
setFileContent(path, fileContent);
}
}
@@ -79,10 +72,6 @@ function TreeNode({ path, defaultOpen = false }: TreeNodeProps) {
type={isDirectory ? "folder" : "file"}
isOpen={isOpen}
/>
-
- {modifiedFiles[path] && (
-
- )}
{isOpen && paths && (
diff --git a/frontend/src/components/shared/buttons/upload-icon-button.tsx b/frontend/src/components/shared/buttons/upload-icon-button.tsx
deleted file mode 100644
index 0e8bdeab29..0000000000
--- a/frontend/src/components/shared/buttons/upload-icon-button.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { IoIosCloudUpload } from "react-icons/io";
-import { IconButton } from "./icon-button";
-
-interface UploadIconButtonProps {
- onClick: () => void;
-}
-
-export function UploadIconButton({ onClick }: UploadIconButtonProps) {
- return (
-
- }
- testId="upload"
- ariaLabel="Upload File"
- onClick={onClick}
- />
- );
-}
diff --git a/frontend/src/context/files.tsx b/frontend/src/context/files.tsx
index cc1f5d7311..13ce579fb8 100644
--- a/frontend/src/context/files.tsx
+++ b/frontend/src/context/files.tsx
@@ -24,10 +24,6 @@ interface FilesContextType {
setFileContent: (path: string, content: string) => void;
selectedPath: string | null;
setSelectedPath: (path: string | null) => void;
- modifiedFiles: Record
;
- modifyFileContent: (path: string, content: string) => void;
- saveFileContent: (path: string) => string | undefined;
- discardChanges: (path: string) => void;
}
const FilesContext = React.createContext(
@@ -41,49 +37,12 @@ interface FilesProviderProps {
function FilesProvider({ children }: FilesProviderProps) {
const [paths, setPaths] = React.useState([]);
const [files, setFiles] = React.useState>({});
- const [modifiedFiles, setModifiedFiles] = React.useState<
- Record
- >({});
const [selectedPath, setSelectedPath] = React.useState(null);
const setFileContent = React.useCallback((path: string, content: string) => {
setFiles((prev) => ({ ...prev, [path]: content }));
}, []);
- const modifyFileContent = React.useCallback(
- (path: string, content: string) => {
- if (files[path] !== content) {
- setModifiedFiles((prev) => ({ ...prev, [path]: content }));
- } else {
- const newModifiedFiles = { ...modifiedFiles };
- delete newModifiedFiles[path];
- setModifiedFiles(newModifiedFiles);
- }
- },
- [files, modifiedFiles],
- );
-
- const discardChanges = React.useCallback((path: string) => {
- setModifiedFiles((prev) => {
- const newModifiedFiles = { ...prev };
- delete newModifiedFiles[path];
- return newModifiedFiles;
- });
- }, []);
-
- const saveFileContent = React.useCallback(
- (path: string): string | undefined => {
- const content = modifiedFiles[path];
- if (content) {
- setFiles((prev) => ({ ...prev, [path]: content }));
- discardChanges(path);
- }
-
- return content;
- },
- [files, modifiedFiles, selectedPath, discardChanges],
- );
-
const value = React.useMemo(
() => ({
paths,
@@ -92,23 +51,8 @@ function FilesProvider({ children }: FilesProviderProps) {
setFileContent,
selectedPath,
setSelectedPath,
- modifiedFiles,
- modifyFileContent,
- saveFileContent,
- discardChanges,
}),
- [
- paths,
- setPaths,
- files,
- setFileContent,
- selectedPath,
- setSelectedPath,
- modifiedFiles,
- modifyFileContent,
- saveFileContent,
- discardChanges,
- ],
+ [paths, setPaths, files, setFileContent, selectedPath, setSelectedPath],
);
return (
diff --git a/frontend/src/hooks/mutation/use-save-file.ts b/frontend/src/hooks/mutation/use-save-file.ts
deleted file mode 100644
index 709b9e7489..0000000000
--- a/frontend/src/hooks/mutation/use-save-file.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { useMutation } from "@tanstack/react-query";
-import toast from "react-hot-toast";
-import OpenHands from "#/api/open-hands";
-import { useConversation } from "#/context/conversation-context";
-
-type SaveFileArgs = {
- path: string;
- content: string;
-};
-
-export const useSaveFile = () => {
- const { conversationId } = useConversation();
- return useMutation({
- mutationFn: ({ path, content }: SaveFileArgs) =>
- OpenHands.saveFile(conversationId, path, content),
- onError: (error) => {
- toast.error(error.message);
- },
- });
-};
diff --git a/frontend/src/hooks/mutation/use-upload-files.ts b/frontend/src/hooks/mutation/use-upload-files.ts
deleted file mode 100644
index 5c8635e736..0000000000
--- a/frontend/src/hooks/mutation/use-upload-files.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { useMutation } from "@tanstack/react-query";
-import OpenHands from "#/api/open-hands";
-import { useConversation } from "#/context/conversation-context";
-
-type UploadFilesArgs = {
- files: File[];
-};
-
-export const useUploadFiles = () => {
- const { conversationId } = useConversation();
- return useMutation({
- mutationFn: ({ files }: UploadFilesArgs) =>
- OpenHands.uploadFiles(conversationId, files),
- });
-};
diff --git a/frontend/src/routes/_oh.app._index/route.tsx b/frontend/src/routes/_oh.app._index/route.tsx
index 007f31145a..39b52005bb 100644
--- a/frontend/src/routes/_oh.app._index/route.tsx
+++ b/frontend/src/routes/_oh.app._index/route.tsx
@@ -1,16 +1,9 @@
import React from "react";
-import { useSelector } from "react-redux";
import { useRouteError } from "react-router";
-import { editor } from "monaco-editor";
-import { EditorProps } from "@monaco-editor/react";
-import { RootState } from "#/store";
-import { AgentState } from "#/types/agent-state";
-import CodeEditorComponent from "../../components/features/editor/code-editor-component";
-import { useFiles } from "#/context/files";
-import { useSaveFile } from "#/hooks/mutation/use-save-file";
-import { ASSET_FILE_TYPES } from "./constants";
-import { EditorActions } from "#/components/features/editor/editor-actions";
+import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
+import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism";
import { FileExplorer } from "#/components/features/file-explorer/file-explorer";
+import { useFiles } from "#/context/files";
export function ErrorBoundary() {
const error = useRouteError();
@@ -23,90 +16,91 @@ export function ErrorBoundary() {
);
}
-function CodeEditor() {
- const {
- selectedPath,
- modifiedFiles,
- saveFileContent: saveNewFileContent,
- discardChanges,
- } = useFiles();
+function getLanguageFromPath(path: string): string {
+ const extension = path.split(".").pop()?.toLowerCase();
+ switch (extension) {
+ case "js":
+ case "jsx":
+ return "javascript";
+ case "ts":
+ case "tsx":
+ return "typescript";
+ case "py":
+ return "python";
+ case "html":
+ return "html";
+ case "css":
+ return "css";
+ case "json":
+ return "json";
+ case "md":
+ return "markdown";
+ case "yml":
+ case "yaml":
+ return "yaml";
+ case "sh":
+ case "bash":
+ return "bash";
+ case "dockerfile":
+ return "dockerfile";
+ case "rs":
+ return "rust";
+ case "go":
+ return "go";
+ case "java":
+ return "java";
+ case "cpp":
+ case "cc":
+ case "cxx":
+ return "cpp";
+ case "c":
+ return "c";
+ case "rb":
+ return "ruby";
+ case "php":
+ return "php";
+ case "sql":
+ return "sql";
+ default:
+ return "text";
+ }
+}
+function FileViewer() {
const [fileExplorerIsOpen, setFileExplorerIsOpen] = React.useState(true);
- const editorRef = React.useRef(null);
-
- const { mutate: saveFile } = useSaveFile();
+ const { selectedPath, files } = useFiles();
const toggleFileExplorer = () => {
setFileExplorerIsOpen((prev) => !prev);
- editorRef.current?.layout({ width: 0, height: 0 });
};
- const handleEditorDidMount: EditorProps["onMount"] = (e, monaco) => {
- editorRef.current = e;
-
- monaco.editor.defineTheme("oh-dark", {
- base: "vs-dark",
- inherit: true,
- rules: [],
- colors: {
- "editor.background": "#171717",
- },
- });
- monaco.editor.setTheme("oh-dark");
- };
-
- const agentState = useSelector(
- (state: RootState) => state.agent.curAgentState,
- );
-
- // Code editing is only allowed when the agent is paused, finished, or awaiting user input (server rules)
- const isEditingAllowed = React.useMemo(
- () =>
- agentState === AgentState.PAUSED ||
- agentState === AgentState.FINISHED ||
- agentState === AgentState.AWAITING_USER_INPUT,
- [agentState],
- );
-
- const handleSave = async () => {
- if (selectedPath) {
- const content = modifiedFiles[selectedPath];
- if (content) {
- saveFile({ path: selectedPath, content });
- saveNewFileContent(selectedPath);
- }
- }
- };
-
- const handleDiscard = () => {
- if (selectedPath) discardChanges(selectedPath);
- };
-
- const isAssetFileType = selectedPath
- ? ASSET_FILE_TYPES.some((ext) => selectedPath.endsWith(ext))
- : false;
-
return (
- {selectedPath && !isAssetFileType && (
+ {selectedPath && (
{selectedPath}
-
)}
-
+ {selectedPath && files[selectedPath] && (
+
+
+ {files[selectedPath]}
+
+
+ )}
);
}
-export default CodeEditor;
+export default FileViewer;
diff --git a/frontend/src/routes/_oh.app/hooks/use-handle-runtime-active.ts b/frontend/src/routes/_oh.app/hooks/use-handle-runtime-active.ts
index 4499162166..08f3c3e5a9 100644
--- a/frontend/src/routes/_oh.app/hooks/use-handle-runtime-active.ts
+++ b/frontend/src/routes/_oh.app/hooks/use-handle-runtime-active.ts
@@ -1,13 +1,9 @@
import React from "react";
-import toast from "react-hot-toast";
-import { useDispatch, useSelector } from "react-redux";
+import { useSelector } from "react-redux";
import { useAuth } from "#/context/auth-context";
import { useWsClient } from "#/context/ws-client-provider";
import { getGitHubTokenCommand } from "#/services/terminal-service";
-import { setImportedProjectZip } from "#/state/initial-query-slice";
import { RootState } from "#/store";
-import { base64ToBlob } from "#/utils/base64-to-blob";
-import { useUploadFiles } from "../../../hooks/mutation/use-upload-files";
import { useGitHubUser } from "../../../hooks/query/use-github-user";
import { isGitHubErrorReponse } from "#/api/github-axios-instance";
import { RUNTIME_INACTIVE_STATES } from "#/types/agent-state";
@@ -17,48 +13,19 @@ export const useHandleRuntimeActive = () => {
const { send } = useWsClient();
const { curAgentState } = useSelector((state: RootState) => state.agent);
- const dispatch = useDispatch();
-
const { data: user } = useGitHubUser();
- const { mutate: uploadFiles } = useUploadFiles();
const runtimeActive = !RUNTIME_INACTIVE_STATES.includes(curAgentState);
- const { importedProjectZip } = useSelector(
- (state: RootState) => state.initialQuery,
- );
-
const userId = React.useMemo(() => {
if (user && !isGitHubErrorReponse(user)) return user.id;
return null;
}, [user]);
- const handleUploadFiles = (zip: string) => {
- const blob = base64ToBlob(zip);
- const file = new File([blob], "imported-project.zip", {
- type: blob.type,
- });
- uploadFiles(
- { files: [file] },
- {
- onError: () => {
- toast.error("Failed to upload project files.");
- },
- },
- );
- dispatch(setImportedProjectZip(null));
- };
-
React.useEffect(() => {
if (runtimeActive && userId && gitHubToken) {
// Export if the user valid, this could happen mid-session so it is handled here
send(getGitHubTokenCommand(gitHubToken));
}
}, [userId, gitHubToken, runtimeActive]);
-
- React.useEffect(() => {
- if (runtimeActive && importedProjectZip) {
- handleUploadFiles(importedProjectZip);
- }
- }, [runtimeActive, importedProjectZip]);
};