mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
refactor(frontend): conversation tab content component (#10956)
This commit is contained in:
parent
e74bbd81d1
commit
d664f516db
@ -2,7 +2,7 @@ import { useSelector } from "react-redux";
|
|||||||
import { useWindowSize } from "@uidotdev/usehooks";
|
import { useWindowSize } from "@uidotdev/usehooks";
|
||||||
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
|
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
|
||||||
import { ChatInterface } from "../chat/chat-interface";
|
import { ChatInterface } from "../chat/chat-interface";
|
||||||
import { ConversationTabContent } from "./conversation-tabs/conversation-tab-content";
|
import { ConversationTabContent } from "./conversation-tabs/conversation-tab-content/conversation-tab-content";
|
||||||
import { cn } from "#/utils/utils";
|
import { cn } from "#/utils/utils";
|
||||||
import { RootState } from "#/store";
|
import { RootState } from "#/store";
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,14 @@
|
|||||||
import { lazy, useMemo } from "react";
|
import { lazy, useMemo } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { cn } from "#/utils/utils";
|
|
||||||
import { RootState } from "#/store";
|
import { RootState } from "#/store";
|
||||||
import { ConversationLoading } from "../conversation-loading";
|
import { ConversationLoading } from "../../conversation-loading";
|
||||||
import Terminal from "../../terminal/terminal";
|
|
||||||
import { ConversationTabTitle } from "./conversation-tab-title";
|
|
||||||
import { I18nKey } from "#/i18n/declaration";
|
import { I18nKey } from "#/i18n/declaration";
|
||||||
|
import { TabWrapper } from "./tab-wrapper";
|
||||||
|
import { TabContainer } from "./tab-container";
|
||||||
|
import { TabContentArea } from "./tab-content-area";
|
||||||
|
import { ConversationTabTitle } from "../conversation-tab-title";
|
||||||
|
import Terminal from "#/components/features/terminal/terminal";
|
||||||
|
|
||||||
// Lazy load all tab components
|
// Lazy load all tab components
|
||||||
const EditorTab = lazy(() => import("#/routes/changes-tab"));
|
const EditorTab = lazy(() => import("#/routes/changes-tab"));
|
||||||
@ -33,6 +35,28 @@ export function ConversationTabContent() {
|
|||||||
const isVSCodeActive = selectedTab === "vscode";
|
const isVSCodeActive = selectedTab === "vscode";
|
||||||
const isTerminalActive = selectedTab === "terminal";
|
const isTerminalActive = selectedTab === "terminal";
|
||||||
|
|
||||||
|
// Define tab configurations
|
||||||
|
const tabs = [
|
||||||
|
{ key: "editor", component: EditorTab, isActive: isEditorActive },
|
||||||
|
{
|
||||||
|
key: "browser",
|
||||||
|
component: BrowserTab,
|
||||||
|
isActive: isBrowserActive,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "jupyter",
|
||||||
|
component: JupyterTab,
|
||||||
|
isActive: isJupyterActive,
|
||||||
|
},
|
||||||
|
{ key: "served", component: ServedTab, isActive: isServedActive },
|
||||||
|
{ key: "vscode", component: VSCodeTab, isActive: isVSCodeActive },
|
||||||
|
{
|
||||||
|
key: "terminal",
|
||||||
|
component: Terminal,
|
||||||
|
isActive: isTerminalActive,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const conversationTabTitle = useMemo(() => {
|
const conversationTabTitle = useMemo(() => {
|
||||||
if (isEditorActive) {
|
if (isEditorActive) {
|
||||||
return t(I18nKey.COMMON$CHANGES);
|
return t(I18nKey.COMMON$CHANGES);
|
||||||
@ -67,69 +91,15 @@ export function ConversationTabContent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<TabContainer>
|
||||||
className={cn(
|
|
||||||
"bg-[#25272D] border border-[#525252] rounded-xl flex flex-col h-full w-full",
|
|
||||||
"h-full w-full",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<ConversationTabTitle title={conversationTabTitle} />
|
<ConversationTabTitle title={conversationTabTitle} />
|
||||||
|
<TabContentArea>
|
||||||
<div className="overflow-hidden flex-grow rounded-b-xl">
|
{tabs.map(({ key, component: Component, isActive }) => (
|
||||||
<div className="h-full w-full">
|
<TabWrapper key={key} isActive={isActive}>
|
||||||
<div className="h-full w-full relative">
|
<Component />
|
||||||
{/* Each tab content is always loaded but only visible when active */}
|
</TabWrapper>
|
||||||
<div
|
))}
|
||||||
className={cn(
|
</TabContentArea>
|
||||||
"absolute inset-0",
|
</TabContainer>
|
||||||
isEditorActive ? "block" : "hidden",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<EditorTab />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"absolute inset-0",
|
|
||||||
isBrowserActive ? "block" : "hidden",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<BrowserTab />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"absolute inset-0",
|
|
||||||
isJupyterActive ? "block" : "hidden",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<JupyterTab />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"absolute inset-0",
|
|
||||||
isServedActive ? "block" : "hidden",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<ServedTab />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"absolute inset-0",
|
|
||||||
isVSCodeActive ? "block" : "hidden",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<VSCodeTab />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"absolute inset-0",
|
|
||||||
isTerminalActive ? "block" : "hidden",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Terminal />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
interface TabContainerProps {
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TabContainer({ children }: TabContainerProps) {
|
||||||
|
return (
|
||||||
|
<div className="bg-[#25272D] border border-[#525252] rounded-xl flex flex-col h-full w-full">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
interface TabContentAreaProps {
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TabContentArea({ children }: TabContentAreaProps) {
|
||||||
|
return (
|
||||||
|
<div className="overflow-hidden flex-grow rounded-b-xl h-full w-full relative">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
import { ReactNode } from "react";
|
||||||
|
import { cn } from "#/utils/utils";
|
||||||
|
|
||||||
|
interface TabWrapperProps {
|
||||||
|
isActive: boolean;
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TabWrapper({ isActive, children }: TabWrapperProps) {
|
||||||
|
return (
|
||||||
|
<div className={cn("absolute inset-0", isActive ? "block" : "hidden")}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user