summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoshanCyriac <roshancyriac.k@gmail.com>2025-07-09 17:35:52 +0530
committerRoshanCyriac <roshancyriac.k@gmail.com>2025-07-09 17:35:52 +0530
commit9e2087f2f8ea189738cb58f52dde587ae3ea230d (patch)
treec4848855be53a9111a10b1655a07d5c3f2e60cc8
parent816a8d16a1454a6c0dc4d1fc61b8cdff781473d5 (diff)
terminal
-rw-r--r--package-lock.json28
-rw-r--r--package.json1
-rw-r--r--src/components/Terminal.jsx95
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>
);
};