mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
Highlight currently selected file (#1725)
* Highlight selected file * Don't recompute context on every render * Fix lint errors
This commit is contained in:
parent
c997289eb7
commit
3abdc231c4
@ -1,7 +1,7 @@
|
||||
import Editor, { Monaco } from "@monaco-editor/react";
|
||||
import { Tab, Tabs } from "@nextui-org/react";
|
||||
import type { editor } from "monaco-editor";
|
||||
import React, { useState } from "react";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { VscCode } from "react-icons/vsc";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
@ -10,10 +10,19 @@ import { selectFile } from "#/services/fileService";
|
||||
import { setCode } from "#/state/codeSlice";
|
||||
import { RootState } from "#/store";
|
||||
import FileExplorer from "./file-explorer/FileExplorer";
|
||||
import { CodeEditorContext } from "./CodeEditorContext";
|
||||
|
||||
function CodeEditor(): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
const [selectedFileName, setSelectedFileName] = useState("");
|
||||
const [selectedFileAbsolutePath, setSelectedFileAbsolutePath] = useState("");
|
||||
const selectedFileName = useMemo(() => {
|
||||
const paths = selectedFileAbsolutePath.split("/");
|
||||
return paths[paths.length - 1];
|
||||
}, [selectedFileAbsolutePath]);
|
||||
const codeEditorContext = useMemo(
|
||||
() => ({ selectedFileAbsolutePath }),
|
||||
[selectedFileAbsolutePath],
|
||||
);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const code = useSelector((state: RootState) => state.code.code);
|
||||
@ -38,10 +47,9 @@ function CodeEditor(): JSX.Element {
|
||||
|
||||
const onSelectFile = async (absolutePath: string) => {
|
||||
const paths = absolutePath.split("/");
|
||||
const fileName = paths[paths.length - 1];
|
||||
const rootlessPath = paths.slice(1).join("/");
|
||||
|
||||
setSelectedFileName(fileName);
|
||||
setSelectedFileAbsolutePath(absolutePath);
|
||||
|
||||
const newCode = await selectFile(rootlessPath);
|
||||
dispatch(setCode(newCode));
|
||||
@ -49,42 +57,44 @@ function CodeEditor(): JSX.Element {
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full bg-neutral-900 transition-all duration-500 ease-in-out">
|
||||
<FileExplorer onFileClick={onSelectFile} />
|
||||
<div className="flex flex-col min-h-0 w-full">
|
||||
<Tabs
|
||||
disableCursorAnimation
|
||||
classNames={{
|
||||
base: "border-b border-divider border-neutral-600 mb-4",
|
||||
tabList:
|
||||
"w-full relative rounded-none bg-neutral-900 p-0 border-divider",
|
||||
cursor: "w-full bg-neutral-600 rounded-none",
|
||||
tab: "max-w-fit px-4 h-[36px]",
|
||||
tabContent: "group-data-[selected=true]:text-white",
|
||||
}}
|
||||
aria-label="Options"
|
||||
>
|
||||
<Tab
|
||||
key={selectedFileName.toLocaleLowerCase()}
|
||||
title={selectedFileName}
|
||||
/>
|
||||
</Tabs>
|
||||
<div className="flex grow items-center justify-center">
|
||||
{selectedFileName === "" ? (
|
||||
<div className="flex flex-col items-center text-neutral-400">
|
||||
<VscCode size={100} />
|
||||
{t(I18nKey.CODE_EDITOR$EMPTY_MESSAGE)}
|
||||
</div>
|
||||
) : (
|
||||
<Editor
|
||||
height="100%"
|
||||
path={selectedFileName.toLocaleLowerCase()}
|
||||
defaultValue=""
|
||||
value={code}
|
||||
onMount={handleEditorDidMount}
|
||||
<CodeEditorContext.Provider value={codeEditorContext}>
|
||||
<FileExplorer onFileClick={onSelectFile} />
|
||||
<div className="flex flex-col min-h-0 w-full">
|
||||
<Tabs
|
||||
disableCursorAnimation
|
||||
classNames={{
|
||||
base: "border-b border-divider border-neutral-600 mb-4",
|
||||
tabList:
|
||||
"w-full relative rounded-none bg-neutral-900 p-0 border-divider",
|
||||
cursor: "w-full bg-neutral-600 rounded-none",
|
||||
tab: "max-w-fit px-4 h-[36px]",
|
||||
tabContent: "group-data-[selected=true]:text-white",
|
||||
}}
|
||||
aria-label="Options"
|
||||
>
|
||||
<Tab
|
||||
key={selectedFileName.toLocaleLowerCase()}
|
||||
title={selectedFileName}
|
||||
/>
|
||||
)}
|
||||
</Tabs>
|
||||
<div className="flex grow items-center justify-center">
|
||||
{selectedFileName === "" ? (
|
||||
<div className="flex flex-col items-center text-neutral-400">
|
||||
<VscCode size={100} />
|
||||
{t(I18nKey.CODE_EDITOR$EMPTY_MESSAGE)}
|
||||
</div>
|
||||
) : (
|
||||
<Editor
|
||||
height="100%"
|
||||
path={selectedFileName.toLocaleLowerCase()}
|
||||
defaultValue=""
|
||||
value={code}
|
||||
onMount={handleEditorDidMount}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CodeEditorContext.Provider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
5
frontend/src/components/CodeEditorContext.ts
Normal file
5
frontend/src/components/CodeEditorContext.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { createContext } from "react";
|
||||
|
||||
export const CodeEditorContext = createContext({
|
||||
selectedFileAbsolutePath: "",
|
||||
});
|
||||
@ -1,7 +1,9 @@
|
||||
import React from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import FolderIcon from "../FolderIcon";
|
||||
import FileIcon from "../FileIcons";
|
||||
import { WorkspaceFile } from "#/services/fileService";
|
||||
import { CodeEditorContext } from "../CodeEditorContext";
|
||||
|
||||
interface TitleProps {
|
||||
name: string;
|
||||
@ -37,6 +39,7 @@ function TreeNode({
|
||||
defaultOpen = false,
|
||||
}: TreeNodeProps) {
|
||||
const [isOpen, setIsOpen] = React.useState(defaultOpen);
|
||||
const { selectedFileAbsolutePath } = React.useContext(CodeEditorContext);
|
||||
|
||||
const handleClick = React.useCallback(() => {
|
||||
if (node.children) {
|
||||
@ -47,7 +50,12 @@ function TreeNode({
|
||||
}, [node, path, onFileClick]);
|
||||
|
||||
return (
|
||||
<div className="text-sm text-neutral-400">
|
||||
<div
|
||||
className={twMerge(
|
||||
"text-sm text-neutral-400",
|
||||
path === selectedFileAbsolutePath ? "bg-gray-700" : "",
|
||||
)}
|
||||
>
|
||||
<Title
|
||||
name={node.name}
|
||||
type={node.children ? "folder" : "file"}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user