mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
feat: make panes resizable (#925)
This commit is contained in:
parent
78858c18b5
commit
707ab7b3f8
@ -3,14 +3,15 @@ import "./App.css";
|
||||
import CogTooth from "./assets/cog-tooth";
|
||||
import ChatInterface from "./components/ChatInterface";
|
||||
import Errors from "./components/Errors";
|
||||
import LoadMessageModal from "./components/LoadMessageModal";
|
||||
import { Container, Orientation } from "./components/Resizable";
|
||||
import SettingModal from "./components/SettingModal";
|
||||
import Terminal from "./components/Terminal";
|
||||
import Workspace from "./components/Workspace";
|
||||
import { fetchMsgTotal } from "./services/session";
|
||||
import LoadMessageModal from "./components/LoadMessageModal";
|
||||
import { ResConfigurations, ResFetchMsgTotal } from "./types/ResponseType";
|
||||
import { fetchConfigurations, saveSettings } from "./services/settingsService";
|
||||
import Socket from "./services/socket";
|
||||
import { ResConfigurations, ResFetchMsgTotal } from "./types/ResponseType";
|
||||
import { getCachedConfig } from "./utils/storage";
|
||||
|
||||
interface Props {
|
||||
@ -78,23 +79,33 @@ function App(): JSX.Element {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-neutral-900 text-white">
|
||||
<LeftNav setSettingOpen={setSettingOpen} />
|
||||
<div className="flex flex-col grow gap-3 py-3 pr-3">
|
||||
<div className="flex gap-3 grow min-h-0">
|
||||
<div className="w-[500px] shrink-0 rounded-xl overflow-hidden border border-neutral-600">
|
||||
<ChatInterface />
|
||||
</div>
|
||||
<div className="flex flex-col flex-1 overflow-hidden rounded-xl bg-neutral-800 border border-neutral-600">
|
||||
<Workspace />
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-72 shrink-0 bg-neutral-800 rounded-xl border border-neutral-600 flex flex-col">
|
||||
<Terminal key="terminal" />
|
||||
</div>
|
||||
<div className="h-screen w-screen flex flex-col">
|
||||
<div className="flex grow bg-neutral-900 text-white min-h-0">
|
||||
<LeftNav setSettingOpen={setSettingOpen} />
|
||||
<Container
|
||||
orientation={Orientation.VERTICAL}
|
||||
className="grow p-3 py-3 pr-3 min-w-0"
|
||||
initialSize={window.innerHeight - 300}
|
||||
firstChild={
|
||||
<Container
|
||||
orientation={Orientation.HORIZONTAL}
|
||||
className="grow h-full min-h-0 min-w-0"
|
||||
initialSize={500}
|
||||
firstChild={<ChatInterface />}
|
||||
firstClassName="min-w-[500px] rounded-xl overflow-hidden border border-neutral-600"
|
||||
secondChild={<Workspace />}
|
||||
secondClassName="flex flex-col overflow-hidden rounded-xl bg-neutral-800 border border-neutral-600 grow min-w-[500px] min-w-[500px]"
|
||||
/>
|
||||
}
|
||||
firstClassName="min-h-72"
|
||||
secondChild={<Terminal key="terminal" />}
|
||||
secondClassName="min-h-72 bg-neutral-800 rounded-xl border border-neutral-600 flex flex-col"
|
||||
/>
|
||||
</div>
|
||||
{/* This div is for the footer that will be added later
|
||||
<div className="h-8 w-full border-t border-border px-2" />
|
||||
*/}
|
||||
<SettingModal isOpen={settingOpen} onClose={handleCloseModal} />
|
||||
|
||||
<LoadMessageModal
|
||||
isOpen={loadMsgWarning}
|
||||
onClose={() => setLoadMsgWarning(false)}
|
||||
|
||||
92
frontend/src/components/Resizable.tsx
Normal file
92
frontend/src/components/Resizable.tsx
Normal file
@ -0,0 +1,92 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export enum Orientation {
|
||||
HORIZONTAL = "horizontal",
|
||||
VERTICAL = "vertical",
|
||||
}
|
||||
|
||||
type ContainerProps = {
|
||||
firstChild: React.ReactNode;
|
||||
firstClassName: string | undefined;
|
||||
secondChild: React.ReactNode;
|
||||
secondClassName: string | undefined;
|
||||
className: string | undefined;
|
||||
orientation: Orientation;
|
||||
initialSize: number;
|
||||
};
|
||||
|
||||
export function Container({
|
||||
firstChild,
|
||||
firstClassName,
|
||||
secondChild,
|
||||
secondClassName,
|
||||
className,
|
||||
orientation,
|
||||
initialSize,
|
||||
}: ContainerProps): JSX.Element {
|
||||
const [firstSize, setFirstSize] = useState<number | undefined>(initialSize);
|
||||
const [dividerPosition, setDividerPosition] = useState<undefined | number>(
|
||||
undefined,
|
||||
);
|
||||
const firstRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (firstRef.current !== null) {
|
||||
if (orientation === Orientation.HORIZONTAL) {
|
||||
firstRef.current.style.width = `${firstSize}px`;
|
||||
} else {
|
||||
firstRef.current.style.height = `${firstSize}px`;
|
||||
}
|
||||
}
|
||||
}, [firstSize, orientation]);
|
||||
|
||||
const onMouseMove = (e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (firstSize && dividerPosition) {
|
||||
if (orientation === Orientation.HORIZONTAL) {
|
||||
const newLeftWidth = firstSize + e.clientX - dividerPosition;
|
||||
setDividerPosition(e.clientX);
|
||||
setFirstSize(newLeftWidth);
|
||||
} else {
|
||||
const newTopHeight = firstSize + e.clientY - dividerPosition;
|
||||
setDividerPosition(e.clientY);
|
||||
setFirstSize(newTopHeight);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseUp = () => {
|
||||
document.removeEventListener("mousemove", onMouseMove);
|
||||
document.removeEventListener("mouseup", onMouseUp);
|
||||
};
|
||||
|
||||
const onMouseDown = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setDividerPosition(
|
||||
orientation === Orientation.HORIZONTAL ? e.clientX : e.clientY,
|
||||
);
|
||||
document.addEventListener("mousemove", onMouseMove);
|
||||
document.addEventListener("mouseup", onMouseUp);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={twMerge(
|
||||
`flex ${orientation === Orientation.HORIZONTAL ? "" : "flex-col"}`,
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div ref={firstRef} className={firstClassName}>
|
||||
{firstChild}
|
||||
</div>
|
||||
<div
|
||||
className={`${orientation === Orientation.VERTICAL ? "cursor-ns-resize h-3" : "cursor-ew-resize w-3"} shrink-0`}
|
||||
onMouseDown={onMouseDown}
|
||||
/>
|
||||
<div className={twMerge(secondClassName, "flex-1")}>{secondChild}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user