Browse Source

feat: start implementing new frontend layout (#737)

Alex Bäuerle 1 year ago
parent
commit
8ec24ab7ea

+ 33 - 7
frontend/src/App.tsx

@@ -1,10 +1,29 @@
 import React, { useState } from "react";
 import React, { useState } from "react";
 import "./App.css";
 import "./App.css";
+import CogTooth from "./assets/cog-tooth";
 import ChatInterface from "./components/ChatInterface";
 import ChatInterface from "./components/ChatInterface";
 import Errors from "./components/Errors";
 import Errors from "./components/Errors";
 import SettingModal from "./components/SettingModal";
 import SettingModal from "./components/SettingModal";
+import Terminal from "./components/Terminal";
 import Workspace from "./components/Workspace";
 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 {
 function App(): JSX.Element {
   const [settingOpen, setSettingOpen] = useState(false);
   const [settingOpen, setSettingOpen] = useState(false);
 
 
@@ -14,15 +33,22 @@ function App(): JSX.Element {
 
 
   return (
   return (
     <div className="flex h-screen bg-bg-dark text-white">
     <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>
-      <div className="flex flex-col flex-1 m-4 overflow-hidden rounded-xl bg-bg-light">
-        <Workspace />
-      </div>
-
       <SettingModal isOpen={settingOpen} onClose={handleCloseModal} />
       <SettingModal isOpen={settingOpen} onClose={handleCloseModal} />
+      <Errors />
     </div>
     </div>
   );
   );
 }
 }

+ 7 - 20
frontend/src/components/ChatInterface.tsx

@@ -1,21 +1,20 @@
 import { Card, CardBody } from "@nextui-org/react";
 import { Card, CardBody } from "@nextui-org/react";
 import React, { useEffect, useRef } from "react";
 import React, { useEffect, useRef } from "react";
-import { useSelector } from "react-redux";
 import { useTranslation } from "react-i18next";
 import { useTranslation } from "react-i18next";
+import { useSelector } from "react-redux";
 import assistantAvatar from "../assets/assistant-avatar.png";
 import assistantAvatar from "../assets/assistant-avatar.png";
-import CogTooth from "../assets/cog-tooth";
 import userAvatar from "../assets/user-avatar.png";
 import userAvatar from "../assets/user-avatar.png";
 import { useTypingEffect } from "../hooks/useTypingEffect";
 import { useTypingEffect } from "../hooks/useTypingEffect";
+import { I18nKey } from "../i18n/declaration";
 import {
 import {
+  addAssistantMessageToChat,
   setCurrentQueueMarkerState,
   setCurrentQueueMarkerState,
   setCurrentTypingMsgState,
   setCurrentTypingMsgState,
   setTypingAcitve,
   setTypingAcitve,
-  addAssistantMessageToChat,
 } from "../services/chatService";
 } from "../services/chatService";
-import { RootState } from "../store";
 import { Message } from "../state/chatSlice";
 import { Message } from "../state/chatSlice";
+import { RootState } from "../store";
 import Input from "./Input";
 import Input from "./Input";
-import { I18nKey } from "../i18n/declaration";
 
 
 interface IChatBubbleProps {
 interface IChatBubbleProps {
   msg: Message;
   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);
   const { initialized } = useSelector((state: RootState) => state.task);
 
 
   return (
   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 />}
       {initialized ? <MessageList /> : <InitializingStatus />}
       <Input />
       <Input />
     </div>
     </div>

+ 10 - 3
frontend/src/components/Terminal.tsx

@@ -1,7 +1,7 @@
-import React, { useEffect, useRef } from "react";
 import { IDisposable, Terminal as XtermTerminal } from "@xterm/xterm";
 import { IDisposable, Terminal as XtermTerminal } from "@xterm/xterm";
-import { FitAddon } from "xterm-addon-fit";
 import "@xterm/xterm/css/xterm.css";
 import "@xterm/xterm/css/xterm.css";
+import React, { useEffect, useRef } from "react";
+import { FitAddon } from "xterm-addon-fit";
 import socket from "../socket/socket";
 import socket from "../socket/socket";
 
 
 class JsonWebsocketAddon {
 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;
 export default Terminal;

+ 11 - 18
frontend/src/components/Workspace.tsx

@@ -1,28 +1,21 @@
-import React, { useMemo, useState } from "react";
 import { Tab, Tabs } from "@nextui-org/react";
 import { Tab, Tabs } from "@nextui-org/react";
+import React, { useMemo, useState } from "react";
 import { useTranslation } from "react-i18next";
 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 Calendar from "../assets/calendar";
 import Earth from "../assets/earth";
 import Earth from "../assets/earth";
 import Pencil from "../assets/pencil";
 import Pencil from "../assets/pencil";
 import { I18nKey } from "../i18n/declaration";
 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() {
 function Workspace() {
   const { t } = useTranslation();
   const { t } = useTranslation();
-  const [activeTab, setActiveTab] = useState<TabType>(TabOption.TERMINAL);
+  const [activeTab, setActiveTab] = useState<TabType>(TabOption.CODE);
 
 
   const tabData = useMemo(
   const tabData = useMemo(
     () => ({
     () => ({
-      [TabOption.TERMINAL]: {
-        name: t(I18nKey.WORKSPACE$TERMINAL_TAB_LABEL),
-        icon: <CmdLine />,
-        component: <Terminal key="terminal" />,
-      },
       [TabOption.PLANNER]: {
       [TabOption.PLANNER]: {
         name: t(I18nKey.WORKSPACE$PLANNER_TAB_LABEL),
         name: t(I18nKey.WORKSPACE$PLANNER_TAB_LABEL),
         icon: <Calendar />,
         icon: <Calendar />,
@@ -44,12 +37,12 @@ function Workspace() {
 
 
   return (
   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
         <Tabs
-          variant="underlined"
+          variant="light"
           size="lg"
           size="lg"
           onSelectionChange={(v) => {
           onSelectionChange={(v) => {
             setActiveTab(v as TabType);
             setActiveTab(v as TabType);

+ 6 - 6
frontend/src/index.css

@@ -1,22 +1,22 @@
 :root {
 :root {
-  --bg-dark: #1e1e1e;
+  --bg-dark: #0c0e10;
   --bg-light: #292929;
   --bg-light: #292929;
   --bg-input: #393939;
   --bg-input: #393939;
-  --bg-workspace: #171717;
+  --bg-workspace: #1f2228;
+  --border: #3c3c4a;
   background-color: var(--bg-dark) !important;
   background-color: var(--bg-dark) !important;
 }
 }
 
 
-
 body {
 body {
   margin: 0;
   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;
     sans-serif;
   -webkit-font-smoothing: antialiased;
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
   -moz-osx-font-smoothing: grayscale;
 }
 }
 
 
 code {
 code {
-  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+  font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
     monospace;
     monospace;
 }
 }

+ 2 - 12
frontend/src/types/TabOption.tsx

@@ -1,21 +1,11 @@
 enum TabOption {
 enum TabOption {
-  TERMINAL = "terminal",
   PLANNER = "planner",
   PLANNER = "planner",
   CODE = "code",
   CODE = "code",
   BROWSER = "browser",
   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 };
 export { AllTabs, TabOption, type TabType };

+ 14 - 12
frontend/tailwind.config.js

@@ -1,23 +1,25 @@
 /** @type {import('tailwindcss').Config} */
 /** @type {import('tailwindcss').Config} */
-const {nextui} = require("@nextui-org/react");
+const { nextui } = require("@nextui-org/react");
 export default {
 export default {
   content: [
   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: {
   theme: {
     extend: {
     extend: {
       colors: {
       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",
   darkMode: "class",
-  plugins: [nextui({
-    defaultTheme: "dark"
-  })],
-}
-
+  plugins: [
+    nextui({
+      defaultTheme: "dark",
+    }),
+  ],
+};