Compare commits

...

3 Commits

Author SHA1 Message Date
openhands
803bec4fc0 Fix TypeScript error in draggable divider implementation 2024-12-13 17:13:31 +00:00
openhands
ef5ee425f7 Add draggable divider between chat and workspace panels 2024-12-13 17:08:34 +00:00
openhands
7733260f43 feat: improve chat layout and move terminal to IDE tabs
- Increase chat window width to 50% for better visibility
- Move terminal from bottom panel to IDE tabs section
- Add terminal route for tab navigation
- Remove height split between IDE and terminal
2024-12-13 15:04:55 +00:00
2 changed files with 71 additions and 22 deletions

View File

@@ -0,0 +1,23 @@
import React from "react";
import { useAuth } from "#/context/auth-context";
function TerminalRoute() {
const { token, gitHubToken } = useAuth();
const Terminal = React.useMemo(
() => React.lazy(() => import("#/components/features/terminal/terminal")),
[],
);
const secrets = React.useMemo(
() => [gitHubToken, token].filter((secret) => secret !== null),
[gitHubToken, token],
);
return (
<React.Suspense fallback={<div className="h-full" />}>
<Terminal secrets={secrets} />
</React.Suspense>
);
}
export default TerminalRoute;

View File

@@ -26,6 +26,34 @@ import { CountBadge } from "#/components/layout/count-badge";
function App() {
const { token, gitHubToken } = useAuth();
const { settings } = useUserPrefs();
const [leftPanelWidth, setLeftPanelWidth] = React.useState(50); // 50% default width
const isDragging = React.useRef(false);
const handleMouseDown = React.useCallback(() => {
isDragging.current = true;
document.body.style.userSelect = "none";
}, []);
const handleMouseUp = React.useCallback(() => {
isDragging.current = false;
document.body.style.userSelect = "";
}, []);
const handleMouseMove = React.useCallback((e: MouseEvent) => {
if (!isDragging.current) return;
const containerWidth = window.innerWidth;
const newWidth = (e.clientX / containerWidth) * 100;
setLeftPanelWidth(Math.min(Math.max(20, newWidth), 80)); // Limit between 20% and 80%
}, []);
React.useEffect(() => {
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mouseup", handleMouseUp);
return () => {
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mouseup", handleMouseUp);
};
}, [handleMouseMove, handleMouseUp]);
const dispatch = useDispatch();
useConversationConfig();
@@ -40,16 +68,6 @@ function App() {
repository: selectedRepository,
});
const secrets = React.useMemo(
() => [gitHubToken, token].filter((secret) => secret !== null),
[gitHubToken, token],
);
const Terminal = React.useMemo(
() => React.lazy(() => import("#/components/features/terminal/terminal")),
[],
);
useEffectOnce(() => {
dispatch(clearMessages());
dispatch(clearTerminal());
@@ -72,14 +90,24 @@ function App() {
>
<EventHandler>
<div className="flex flex-col h-full gap-3">
<div className="flex h-full overflow-auto gap-3">
<Container className="w-full md:w-[390px] max-h-full relative">
<ChatInterface />
</Container>
<div className="flex h-full overflow-auto">
<div
className="w-full md:w-auto max-h-full relative"
style={{ width: `${leftPanelWidth}%` }}
>
<Container className="h-full">
<ChatInterface />
</Container>
</div>
<div
className="hidden md:block w-1 bg-default-100 hover:bg-default-200 cursor-col-resize"
onMouseDown={handleMouseDown}
/>
<div className="hidden md:flex flex-col grow gap-3">
<Container
className="h-2/3"
className="h-full"
labels={[
{ label: "Workspace", to: "", icon: <CodeIcon /> },
{ label: "Jupyter", to: "jupyter", icon: <ListIcon /> },
@@ -93,19 +121,17 @@ function App() {
to: "browser",
icon: <GlobeIcon />,
},
{
label: "Terminal",
to: "terminal",
icon: <CodeIcon />,
},
]}
>
<FilesProvider>
<Outlet />
</FilesProvider>
</Container>
{/* Terminal uses some API that is not compatible in a server-environment. For this reason, we lazy load it to ensure
* that it loads only in the client-side. */}
<Container className="h-1/3 overflow-scroll" label="Terminal">
<React.Suspense fallback={<div className="h-full" />}>
<Terminal secrets={secrets} />
</React.Suspense>
</Container>
</div>
</div>