diff options
| author | RoshanCyriac <roshancyriac.k@gmail.com> | 2025-07-09 17:35:52 +0530 |
|---|---|---|
| committer | RoshanCyriac <roshancyriac.k@gmail.com> | 2025-07-09 17:35:52 +0530 |
| commit | 9e2087f2f8ea189738cb58f52dde587ae3ea230d (patch) | |
| tree | c4848855be53a9111a10b1655a07d5c3f2e60cc8 | |
| parent | 816a8d16a1454a6c0dc4d1fc61b8cdff781473d5 (diff) | |
terminal
| -rw-r--r-- | package-lock.json | 28 | ||||
| -rw-r--r-- | package.json | 1 | ||||
| -rw-r--r-- | src/components/Terminal.jsx | 95 |
3 files changed, 117 insertions, 7 deletions
diff --git a/package-lock.json b/package-lock.json index 3e6e28c..a2fccfc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "lucide-react": "^0.468.0", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-draggable": "^4.5.0", "react-router-dom": "^6.28.0", "react-syntax-highlighter": "^15.5.0", "react-type-animation": "^3.2.0", @@ -1222,6 +1223,14 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1948,7 +1957,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "peer": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -2051,7 +2059,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -2205,7 +2212,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "peer": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -2252,11 +2258,23 @@ "react": "^19.1.0" } }, + "node_modules/react-draggable": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.5.0.tgz", + "integrity": "sha512-VC+HBLEZ0XJxnOxVAZsdRi8rD04Iz3SiiKOoYzamjylUcju/hP9np/aZdLHf/7WOD268WMoNJMvYfB5yAK45cw==", + "dependencies": { + "clsx": "^2.1.1", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": ">= 16.3.0", + "react-dom": ">= 16.3.0" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "peer": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-refresh": { "version": "0.17.0", diff --git a/package.json b/package.json index 3df314a..1c4131e 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "lucide-react": "^0.468.0", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-draggable": "^4.5.0", "react-router-dom": "^6.28.0", "react-syntax-highlighter": "^15.5.0", "react-type-animation": "^3.2.0", diff --git a/src/components/Terminal.jsx b/src/components/Terminal.jsx index db5c7b0..feaa948 100644 --- a/src/components/Terminal.jsx +++ b/src/components/Terminal.jsx @@ -15,13 +15,35 @@ const DATA = { const SECTIONS = ['home', 'about', 'projects', 'team', 'resources', 'events', 'contact']; const PROMPT_BASE = 'foss@cusat'; +const TERMINAL_WIDTH = 1000; +const TERMINAL_HEIGHT = 500; const Terminal = () => { const xtermRef = useRef(null); const termRef = useRef(null); + const containerRef = useRef(null); + + // Center horizontally, further below the heading + const getInitialPosition = () => { + const x = Math.max((window.innerWidth - TERMINAL_WIDTH) / 2, 20); + const y = window.scrollY + 350; + return { x, y, dragging: false, offsetX: 0, offsetY: 0 }; + }; + + const [drag, setDrag] = useState(getInitialPosition()); const [cwd, setCwd] = useState('home'); useEffect(() => { + // Recenter on window resize (optional, only if not dragging) + const handleResize = () => { + if (!drag.dragging) setDrag(getInitialPosition()); + }; + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + // eslint-disable-next-line + }, []); + + useEffect(() => { if (!xtermRef.current) { const term = new XTerm({ theme: { @@ -71,6 +93,43 @@ const Terminal = () => { }; }, []); + // Drag logic + useEffect(() => { + function onMouseMove(e) { + if (drag.dragging) { + setDrag((prev) => ({ + ...prev, + x: e.clientX - prev.offsetX, + y: e.clientY - prev.offsetY, + })); + } + } + function onMouseUp() { + setDrag((prev) => ({ ...prev, dragging: false })); + } + if (drag.dragging) { + window.addEventListener('mousemove', onMouseMove); + window.addEventListener('mouseup', onMouseUp); + } else { + window.removeEventListener('mousemove', onMouseMove); + window.removeEventListener('mouseup', onMouseUp); + } + return () => { + window.removeEventListener('mousemove', onMouseMove); + window.removeEventListener('mouseup', onMouseUp); + }; + }, [drag.dragging]); + + function startDrag(e) { + const rect = containerRef.current.getBoundingClientRect(); + setDrag({ + ...drag, + dragging: true, + offsetX: e.clientX - rect.left, + offsetY: e.clientY - rect.top, + }); + } + function printPrompt(term, dir) { term.write(`\x1b[1;32m${PROMPT_BASE}:${dir === 'home' ? '~' : '/' + dir}$ \x1b[0m`); } @@ -112,8 +171,40 @@ const Terminal = () => { } return ( - <div style={{ width: '100%', maxWidth: 1000, margin: '2rem auto', background: '#161b22', borderRadius: 8, boxShadow: '0 2px 16px rgba(0,0,0,0.2)' }}> - <div ref={termRef} style={{ height: 500, width: '100%' }} /> + <div + ref={containerRef} + style={{ + width: '100%', + maxWidth: TERMINAL_WIDTH, + position: 'fixed', + top: drag.y, + left: drag.x, + zIndex: 9999, + background: '#161b22', + borderRadius: 8, + boxShadow: '0 2px 16px rgba(0,0,0,0.2)', + userSelect: drag.dragging ? 'none' : 'auto', + cursor: drag.dragging ? 'move' : 'default', + }} + > + <div + className="terminal-drag-bar" + style={{ + cursor: 'move', + background: '#222', + color: '#00ff41', + padding: '0.5rem 1rem', + borderTopLeftRadius: 8, + borderTopRightRadius: 8, + fontWeight: 'bold', + letterSpacing: 1, + userSelect: 'none', + }} + onMouseDown={startDrag} + > + FOSS CUSAT Terminal (Drag me) + </div> + <div ref={termRef} style={{ height: TERMINAL_HEIGHT, width: '100%' }} /> </div> ); }; |
