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 { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
|
||||
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 { RootState } from "#/store";
|
||||
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
import { lazy, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSelector } from "react-redux";
|
||||
import { cn } from "#/utils/utils";
|
||||
import { RootState } from "#/store";
|
||||
import { ConversationLoading } from "../conversation-loading";
|
||||
import Terminal from "../../terminal/terminal";
|
||||
import { ConversationTabTitle } from "./conversation-tab-title";
|
||||
import { ConversationLoading } from "../../conversation-loading";
|
||||
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
|
||||
const EditorTab = lazy(() => import("#/routes/changes-tab"));
|
||||
@ -33,6 +35,28 @@ export function ConversationTabContent() {
|
||||
const isVSCodeActive = selectedTab === "vscode";
|
||||
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(() => {
|
||||
if (isEditorActive) {
|
||||
return t(I18nKey.COMMON$CHANGES);
|
||||
@ -67,69 +91,15 @@ export function ConversationTabContent() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"bg-[#25272D] border border-[#525252] rounded-xl flex flex-col h-full w-full",
|
||||
"h-full w-full",
|
||||
)}
|
||||
>
|
||||
<TabContainer>
|
||||
<ConversationTabTitle title={conversationTabTitle} />
|
||||
|
||||
<div className="overflow-hidden flex-grow rounded-b-xl">
|
||||
<div className="h-full w-full">
|
||||
<div className="h-full w-full relative">
|
||||
{/* Each tab content is always loaded but only visible when active */}
|
||||
<div
|
||||
className={cn(
|
||||
"absolute inset-0",
|
||||
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>
|
||||
<TabContentArea>
|
||||
{tabs.map(({ key, component: Component, isActive }) => (
|
||||
<TabWrapper key={key} isActive={isActive}>
|
||||
<Component />
|
||||
</TabWrapper>
|
||||
))}
|
||||
</TabContentArea>
|
||||
</TabContainer>
|
||||
);
|
||||
}
|
||||
@ -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