mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
feat: start implementing new frontend layout (#737)
This commit is contained in:
parent
5ec0e5b7ec
commit
8ec24ab7ea
@ -1,10 +1,29 @@
|
||||
import React, { useState } from "react";
|
||||
import "./App.css";
|
||||
import CogTooth from "./assets/cog-tooth";
|
||||
import ChatInterface from "./components/ChatInterface";
|
||||
import Errors from "./components/Errors";
|
||||
import SettingModal from "./components/SettingModal";
|
||||
import Terminal from "./components/Terminal";
|
||||
import Workspace from "./components/Workspace";
|
||||
|
||||
interface Props {
|
||||
setSettingOpen: (isOpen: boolean) => void;
|
||||
}
|
||||
|
||||
function LeftNav({ setSettingOpen }: Props): JSX.Element {
|
||||
return (
|
||||
<div className="flex flex-col h-full p-4 bg-bg-dark w-16 items-center shrink-0">
|
||||
<div
|
||||
className="mt-auto cursor-pointer hover:opacity-80"
|
||||
onClick={() => setSettingOpen(true)}
|
||||
>
|
||||
<CogTooth />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function App(): JSX.Element {
|
||||
const [settingOpen, setSettingOpen] = useState(false);
|
||||
|
||||
@ -14,15 +33,22 @@ function App(): JSX.Element {
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-bg-dark text-white">
|
||||
<Errors />
|
||||
<div className="flex-1 rounded-xl m-4 overflow-hidden bg-bg-light">
|
||||
<ChatInterface setSettingOpen={setSettingOpen} />
|
||||
<LeftNav setSettingOpen={setSettingOpen} />
|
||||
<div className="flex flex-col grow gap-3 py-3 pr-3">
|
||||
<div className="flex gap-3 grow">
|
||||
<div className="w-[500px] shrink-0 rounded-xl overflow-hidden border border-border">
|
||||
<ChatInterface />
|
||||
</div>
|
||||
<div className="flex flex-col flex-1 overflow-hidden rounded-xl bg-bg-workspace border border-border">
|
||||
<Workspace />
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-72 shrink-0 bg-bg-workspace rounded-xl border border-border flex flex-col">
|
||||
<Terminal key="terminal" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col flex-1 m-4 overflow-hidden rounded-xl bg-bg-light">
|
||||
<Workspace />
|
||||
</div>
|
||||
|
||||
<SettingModal isOpen={settingOpen} onClose={handleCloseModal} />
|
||||
<Errors />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,21 +1,20 @@
|
||||
import { Card, CardBody } from "@nextui-org/react";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSelector } from "react-redux";
|
||||
import assistantAvatar from "../assets/assistant-avatar.png";
|
||||
import CogTooth from "../assets/cog-tooth";
|
||||
import userAvatar from "../assets/user-avatar.png";
|
||||
import { useTypingEffect } from "../hooks/useTypingEffect";
|
||||
import { I18nKey } from "../i18n/declaration";
|
||||
import {
|
||||
addAssistantMessageToChat,
|
||||
setCurrentQueueMarkerState,
|
||||
setCurrentTypingMsgState,
|
||||
setTypingAcitve,
|
||||
addAssistantMessageToChat,
|
||||
} from "../services/chatService";
|
||||
import { RootState } from "../store";
|
||||
import { Message } from "../state/chatSlice";
|
||||
import { RootState } from "../store";
|
||||
import Input from "./Input";
|
||||
import { I18nKey } from "../i18n/declaration";
|
||||
|
||||
interface IChatBubbleProps {
|
||||
msg: Message;
|
||||
@ -185,24 +184,12 @@ function InitializingStatus(): JSX.Element {
|
||||
);
|
||||
}
|
||||
|
||||
interface Props {
|
||||
setSettingOpen: (isOpen: boolean) => void;
|
||||
}
|
||||
|
||||
function ChatInterface({ setSettingOpen }: Props): JSX.Element {
|
||||
function ChatInterface(): JSX.Element {
|
||||
const { initialized } = useSelector((state: RootState) => state.task);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full p-0 bg-bg-light">
|
||||
<div className="w-full flex justify-between p-5">
|
||||
<div />
|
||||
<div
|
||||
className="cursor-pointer hover:opacity-80"
|
||||
onClick={() => setSettingOpen(true)}
|
||||
>
|
||||
<CogTooth />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col h-full p-0 bg-bg-workspace">
|
||||
<div className="border-b border-border text-lg px-4 py-2">Chat</div>
|
||||
{initialized ? <MessageList /> : <InitializingStatus />}
|
||||
<Input />
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { IDisposable, Terminal as XtermTerminal } from "@xterm/xterm";
|
||||
import { FitAddon } from "xterm-addon-fit";
|
||||
import "@xterm/xterm/css/xterm.css";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { FitAddon } from "xterm-addon-fit";
|
||||
import socket from "../socket/socket";
|
||||
|
||||
class JsonWebsocketAddon {
|
||||
@ -88,7 +88,14 @@ function Terminal(): JSX.Element {
|
||||
};
|
||||
}, []);
|
||||
|
||||
return <div ref={terminalRef} className="h-full w-full block" />;
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="px-4 py-2 text-lg border-b border-border">Terminal</div>
|
||||
<div className="grow p-2 flex min-h-0">
|
||||
<div ref={terminalRef} className="h-full w-full" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Terminal;
|
||||
|
||||
@ -1,28 +1,21 @@
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { Tab, Tabs } from "@nextui-org/react";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Terminal from "./Terminal";
|
||||
import Planner from "./Planner";
|
||||
import CodeEditor from "./CodeEditor";
|
||||
import Browser from "./Browser";
|
||||
import { TabType, TabOption, AllTabs } from "../types/TabOption";
|
||||
import CmdLine from "../assets/cmd-line";
|
||||
import Calendar from "../assets/calendar";
|
||||
import Earth from "../assets/earth";
|
||||
import Pencil from "../assets/pencil";
|
||||
import { I18nKey } from "../i18n/declaration";
|
||||
import { AllTabs, TabOption, TabType } from "../types/TabOption";
|
||||
import Browser from "./Browser";
|
||||
import CodeEditor from "./CodeEditor";
|
||||
import Planner from "./Planner";
|
||||
|
||||
function Workspace() {
|
||||
const { t } = useTranslation();
|
||||
const [activeTab, setActiveTab] = useState<TabType>(TabOption.TERMINAL);
|
||||
const [activeTab, setActiveTab] = useState<TabType>(TabOption.CODE);
|
||||
|
||||
const tabData = useMemo(
|
||||
() => ({
|
||||
[TabOption.TERMINAL]: {
|
||||
name: t(I18nKey.WORKSPACE$TERMINAL_TAB_LABEL),
|
||||
icon: <CmdLine />,
|
||||
component: <Terminal key="terminal" />,
|
||||
},
|
||||
[TabOption.PLANNER]: {
|
||||
name: t(I18nKey.WORKSPACE$PLANNER_TAB_LABEL),
|
||||
icon: <Calendar />,
|
||||
@ -44,12 +37,12 @@ function Workspace() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-full p-4 text-2xl font-bold select-none">
|
||||
{t(I18nKey.WORKSPACE$TITLE)}
|
||||
</div>
|
||||
<div role="tablist" className="tabs tabs-bordered tabs-lg ">
|
||||
<div
|
||||
role="tablist"
|
||||
className="tabs tabs-bordered tabs-lg border-b border-border"
|
||||
>
|
||||
<Tabs
|
||||
variant="underlined"
|
||||
variant="light"
|
||||
size="lg"
|
||||
onSelectionChange={(v) => {
|
||||
setActiveTab(v as TabType);
|
||||
|
||||
@ -1,22 +1,22 @@
|
||||
:root {
|
||||
--bg-dark: #1e1e1e;
|
||||
--bg-dark: #0c0e10;
|
||||
--bg-light: #292929;
|
||||
--bg-input: #393939;
|
||||
--bg-workspace: #171717;
|
||||
--bg-workspace: #1f2228;
|
||||
--border: #3c3c4a;
|
||||
background-color: var(--bg-dark) !important;
|
||||
}
|
||||
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
||||
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||
monospace;
|
||||
}
|
||||
|
||||
@ -1,21 +1,11 @@
|
||||
enum TabOption {
|
||||
TERMINAL = "terminal",
|
||||
PLANNER = "planner",
|
||||
CODE = "code",
|
||||
BROWSER = "browser",
|
||||
}
|
||||
|
||||
type TabType =
|
||||
| TabOption.TERMINAL
|
||||
| TabOption.PLANNER
|
||||
| TabOption.CODE
|
||||
| TabOption.BROWSER;
|
||||
type TabType = TabOption.PLANNER | TabOption.CODE | TabOption.BROWSER;
|
||||
|
||||
const AllTabs = [
|
||||
TabOption.TERMINAL,
|
||||
TabOption.PLANNER,
|
||||
TabOption.CODE,
|
||||
TabOption.BROWSER,
|
||||
];
|
||||
const AllTabs = [TabOption.PLANNER, TabOption.CODE, TabOption.BROWSER];
|
||||
|
||||
export { AllTabs, TabOption, type TabType };
|
||||
|
||||
@ -1,23 +1,25 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
const {nextui} = require("@nextui-org/react");
|
||||
const { nextui } = require("@nextui-org/react");
|
||||
export default {
|
||||
content: [
|
||||
'./src/**/*.{js,ts,jsx,tsx}',
|
||||
"./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}"
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
"./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
'bg-dark': 'var(--bg-dark)',
|
||||
'bg-light': 'var(--bg-light)',
|
||||
'bg-input': 'var(--bg-input)',
|
||||
'bg-workspace': 'var(--bg-workspace)'
|
||||
"bg-dark": "var(--bg-dark)",
|
||||
"bg-light": "var(--bg-light)",
|
||||
"bg-input": "var(--bg-input)",
|
||||
"bg-workspace": "var(--bg-workspace)",
|
||||
border: "var(--border)",
|
||||
},
|
||||
},
|
||||
},
|
||||
darkMode: "class",
|
||||
plugins: [nextui({
|
||||
defaultTheme: "dark"
|
||||
})],
|
||||
}
|
||||
|
||||
plugins: [
|
||||
nextui({
|
||||
defaultTheme: "dark",
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user