diff --git a/docker-compose.yaml b/docker-compose.yaml index cf0d0282..880c8675 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -153,7 +153,7 @@ services: - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf depends_on: - server - - frontend +# - frontend redis: hostname: redis @@ -175,23 +175,23 @@ services: volumes: - workspaces:/workspaces - frontend: - container_name: frontend - build: - context: ./gui - dockerfile: Dockerfile - ports: - - 3000:3000 - volumes: - - ./gui:/app - - /app/node_modules/ - - /app/.next/ - depends_on: - - server - environment: - - NEXT_PUBLIC_DOCKER=true - restart: always - command: [ "yarn", "dev" ] +# frontend: +# container_name: frontend +# build: +# context: ./gui +# dockerfile: Dockerfile +# ports: +# - 3000:3000 +# volumes: +# - ./gui:/app +# - /app/node_modules/ +# - /app/.next/ +# depends_on: +# - server +# environment: +# - NEXT_PUBLIC_DOCKER=true +# restart: always +# command: [ "yarn", "dev" ] gitness: container_name: gitness diff --git a/gui/package.json b/gui/package.json index 2aa1006d..e68d58c1 100644 --- a/gui/package.json +++ b/gui/package.json @@ -12,6 +12,8 @@ "dependencies": { "@monaco-editor/react": "^4.6.0", "@nextui-org/react": "^2.3.6", + "@xterm/addon-fit": "^0.10.0", + "@xterm/xterm": "^5.5.0", "axios": "^1.7.2", "babel-plugin-styled-components": "^2.1.4", "cookie": "^0.6.0", @@ -33,7 +35,8 @@ "react-transition-group": "^4.4.5", "sharp": "^0.33.4", "socket.io-client": "^2.5.0", - "styled-components": "^6.1.11" + "styled-components": "^6.1.11", + "xterm": "^5.3.0" }, "devDependencies": { "@types/cookie": "^0.6.0", @@ -45,6 +48,7 @@ "@types/react-transition-group": "^4.4.10", "@types/socket.io-client": "^3.0.0", "@types/styled-components": "^5.1.34", + "@types/xterm": "^3.0.0", "@typescript-eslint/eslint-plugin": "^6", "@typescript-eslint/parser": "^6", "eslint": "^8", diff --git a/gui/src/app/(programmer)/board/page.tsx b/gui/src/app/(programmer)/board/page.tsx index 18028c6b..fb2b1e4d 100644 --- a/gui/src/app/(programmer)/board/page.tsx +++ b/gui/src/app/(programmer)/board/page.tsx @@ -14,6 +14,7 @@ import SetupModelModal from '@/components/StoryComponents/SetupModelModal'; import CustomLoaders from '@/components/CustomLoaders/CustomLoaders'; import { SkeletonTypes } from '@/app/constants/SkeletonConstants'; import CustomInput from '@/components/CustomInput/CustomInput'; +import TerminalComponent from '@/components/Terminal/Terminal'; const TaskItem = ({ task, handleStoryClick }) => (
{ - toGetAllStoriesOfProject(); - }; - useEffect(() => { toGetAllStoriesOfProject(); }, [searchValue]); @@ -125,7 +122,6 @@ export default function Board() { toGetAllStoriesOfProject={toGetAllStoriesOfProject} /> - setOpenStoryDetailsModal(false)} @@ -151,7 +147,6 @@ export default function Board() { openModal={openSetupModelModal} setOpenModel={setOpenSetupModelModal} /> -
+ +
{ + const terminalRef = useTerminal(commands); + + return ( +
+
+
+ ); +}; + +export default TerminalComponent; diff --git a/gui/src/components/Terminal/terminal.module.css b/gui/src/components/Terminal/terminal.module.css new file mode 100644 index 00000000..a525d8d7 --- /dev/null +++ b/gui/src/components/Terminal/terminal.module.css @@ -0,0 +1,9 @@ +.terminal { + height: 100%; + width: 100%; + border: 1px solid var(--white-opacity-8); + background: var(--layout-bg-color); + color: #ffffff; + padding: 10px; + border-radius: 8px; +} diff --git a/gui/src/hooks/useTerminal.tsx b/gui/src/hooks/useTerminal.tsx new file mode 100644 index 00000000..09b69f40 --- /dev/null +++ b/gui/src/hooks/useTerminal.tsx @@ -0,0 +1,66 @@ +import { useEffect, useRef } from 'react'; +import { Terminal } from '@xterm/xterm'; +import '@xterm/xterm/css/xterm.css'; + +export const useTerminal = (commands: { [key: string]: string }) => { + const terminalRef = useRef(null); + const xtermRef = useRef(null); + + useEffect(() => { + if (terminalRef.current && !xtermRef.current) { + const xterm = new Terminal({ + cursorBlink: true, + theme: { + background: '#1e1e1e', // Dark background + foreground: '#ffffff', // Light text + }, + }); + xterm.open(terminalRef.current); + xtermRef.current = xterm; + + xterm.onKey(({ key, domEvent }) => { + const printable = + !domEvent.altKey && !domEvent.ctrlKey && !domEvent.metaKey; + + if (domEvent.keyCode === 13) { + handleCommand(xterm); + } else if (domEvent.keyCode === 8) { + // Do not delete the prompt + if (xterm.buffer.active.cursorX > 2) { + xterm.write('\b \b'); + } + } else if (printable) { + xterm.write(key); + } + }); + + xterm.prompt = () => { + xterm.write('\r\n$ '); + }; + + xterm.prompt(); + } + + return () => { + xtermRef.current?.dispose(); + }; + }, []); + + const handleCommand = (xterm: Terminal) => { + const input = xterm.buffer.active + .getLine(xterm.buffer.active.cursorY) + ?.translateToString() + .trim(); + const command = input?.split('$ ')[1]; + + if (command && commands[command]) { + xterm.write(`\r\n${commands[command]}`); + } else { + xterm.write('\r\nCommand not found'); + } + + xterm.prompt(); + }; + + return terminalRef; +}; diff --git a/gui/yarn.lock b/gui/yarn.lock index 5ac0734e..05bf7b87 100644 --- a/gui/yarn.lock +++ b/gui/yarn.lock @@ -2500,6 +2500,13 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.10.tgz#04ffa7f406ab628f7f7e97ca23e290cd8ab15efc" integrity sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA== +"@types/xterm@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/xterm/-/xterm-3.0.0.tgz#5b177010903ee1dc26fdc25d6e4e1f2490d44ce6" + integrity sha512-+VaAJQmE7E1d1ebkIh/Zdc2mbXBVwxZGGSgqwzDPpk/HKo0mNT+iX5ZrnswztHSV+CDV+bURl7Yg7PWF7IZfXQ== + dependencies: + xterm "*" + "@typescript-eslint/eslint-plugin@^6": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3" @@ -2637,6 +2644,16 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== +"@xterm/addon-fit@^0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@xterm/addon-fit/-/addon-fit-0.10.0.tgz#bebf87fadd74e3af30fdcdeef47030e2592c6f55" + integrity sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ== + +"@xterm/xterm@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@xterm/xterm/-/xterm-5.5.0.tgz#275fb8f6e14afa6e8a0c05d4ebc94523ff775396" + integrity sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A== + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -6922,6 +6939,11 @@ xtend@^4.0.0: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== +xterm@*, xterm@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0.tgz#867daf9cc826f3d45b5377320aabd996cb0fce46" + integrity sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"