Release 1.0 (#3)

* Nano and timeout error fix #1

* Nano and timeout error fix #2

* Nano and timeout error fix #3

* Nano and timeout error fix #4

* Nano and timeout error fix #5

* Nano and timeout error fix #6

* Nano and timeout error fix #7 (plus css changes for hide buttons)

* Nano and timeout error fix #8 (zoom method)

* Nano zoom fix #9

* Nano zoom fix #10

* Nano zoom fix #11

* Nano zoom fix #12

* Nano zoom fix #13

* Nano zoom fix #14

* Nano zoom fix #15

* Nano zoom fix #16 (ssh-2-promise)

* Nano zoom fix #17 (ssh-2-promise port  fix)

* Full return back to old code with minor fixes

* Terminal size fix #18 (rows & cols fix)

* Test ntfy build notification system

* Silent resize cmds & Auto resize terminal #1

* Silent resize cmds & Auto resize terminal #2

* Docker build update

* Docker build update #2

* Docker build update #3

* Docker build update #4 (cache and cleanup)

* Docker build update #5 (cache and cleanup)

* Docker build update #6 (cache and cleanup)

* Docker build update #7 (final)

* Docker build update #8 (nevermind not finanl)

* Docker build update #9 (nevermind not finanl)

* Docker build update #10

* Docker build update #10 (I hope final)

* Docker build update #10 (actual final)

* Release 1.0!!!!

* Repo clean-up

* Remove files ignored by .gitignore

* Change project name, create logo, change README.md to be soon updated.

* Update README.md #1 (also added MIT License)

* Update README.md #2

* Update README.md #3

* Update README.md #4

* Update README.md #5

* Update README.md #6

* Update README.md #7

* Update README.md

* Add images

* Update image name

* Update README.md

* Final changes to release-1.0

* Update README.md

* Update name (need to fix logo)

* Final updates (I hope for real this time)

* Fix web-socket timeout.

* Fix timeout on close.
This commit was merged in pull request #3.
This commit is contained in:
Karmaa
2024-12-12 18:46:43 -06:00
committed by GitHub
parent b6a3f881a8
commit 15e1fd215e
37 changed files with 926 additions and 278 deletions

View File

@@ -22,7 +22,6 @@
flex-direction: column;
align-items: flex-start;
justify-content: space-between;
border-radius: 5px;
position: relative;
}
@@ -63,7 +62,7 @@
.hide-sidebar-button {
position: fixed;
bottom: 10px;
right: 10px;
right: 23px;
background-color: rgb(108, 108, 108);
color: white;
border: none;

View File

@@ -1,7 +1,7 @@
import React, { useEffect, useRef, useState } from 'react';
import { Terminal } from 'xterm';
import { Terminal } from '@xterm/xterm';
import 'xterm/css/xterm.css';
import { FitAddon } from 'xterm-addon-fit';
import { FitAddon } from '@xterm/addon-fit';
import './App.css';
const App = () => {
@@ -10,13 +10,13 @@ const App = () => {
const fitAddon = useRef(null);
const socket = useRef(null);
const [host, setHost] = useState('');
const [port, setPort] = useState('22');
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [isConnected, setIsConnected] = useState(false);
const [isSideBarHidden, setIsSideBarHidden] = useState(false);
useEffect(() => {
// Initialize the terminal and the fit addon
terminal.current = new Terminal({
cursorBlink: true,
theme: {
@@ -25,23 +25,37 @@ const App = () => {
},
macOptionIsMeta: true,
allowProposedApi: true,
scrollback: 1000, // Allow scrollback so the terminal doesn't lose state
scrollback: 5000,
});
// Initialize and attach the fit addon to the terminal
fitAddon.current = new FitAddon();
terminal.current.loadAddon(fitAddon.current);
terminal.current.open(terminalRef.current);
// Resize terminal to fit the container initially
fitAddon.current.fit();
// Adjust terminal size on window resize
const resizeListener = () => {
// Fit the terminal and send the size when needed
const fitAndNotifyResize = () => {
fitAddon.current.fit();
if (socket.current && socket.current.readyState === WebSocket.OPEN) {
socket.current.send(JSON.stringify({
cols: terminal.current.cols,
rows: terminal.current.rows,
}));
}
};
window.addEventListener('resize', resizeListener);
window.addEventListener('resize', fitAndNotifyResize);
terminal.current.onResize(({ cols, rows }) => {
console.log(`Terminal resized to cols:${cols}, rows:${rows}`);
fitAndNotifyResize();
});
const handleConnectionEstablished = () => {
fitAndNotifyResize();
};
window.addEventListener('connection-established', handleConnectionEstablished);
// Monitor terminal data (activity)
terminal.current.onData((data) => {
@@ -50,50 +64,54 @@ const App = () => {
}
});
// Add specific resize call for certain programs like nano or vim
const resizeTerminalOnStart = () => {
// Resize immediately after starting vim/nano or other programs
fitAddon.current.fit();
terminal.current.clear();
};
terminal.current.onData((data) => {
if (data.includes('nano') || data.includes('vim')) {
// Trigger resize immediately when these programs start
resizeTerminalOnStart();
}
});
// Cleanup on component unmount
return () => {
terminal.current.dispose();
if (socket.current) {
socket.current.close();
}
window.removeEventListener('resize', resizeListener);
window.removeEventListener('resize', fitAndNotifyResize);
};
}, []);
const handleConnect = () => {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}/ws/`; // Use current host and "/ws/" endpoint
socket.current = new WebSocket(wsUrl);
if (!host || !username || !password) {
terminal.current.writeln('Please fill in all fields.');
return;
}
socket.current = new WebSocket("ws://localhost:8081");
socket.current.onopen = () => {
terminal.current.writeln(`Connected to WebSocket server at ${wsUrl}`);
socket.current.send(JSON.stringify({ host, username, password }));
socket.current.send(
JSON.stringify({
host,
port,
username,
password,
rows: terminal.current.rows,
cols: terminal.current.cols
})
);
// Dispatch a custom event when connection is open
const event = new Event('connection-established');
window.dispatchEvent(event);
setIsConnected(true);
};
socket.current.onmessage = (event) => {
terminal.current.write(event.data);
};
socket.current.onerror = (error) => {
terminal.current.writeln(`WebSocket error: ${error.message}`);
};
socket.current.onclose = () => {
terminal.current.writeln('Disconnected from WebSocket server.');
setIsConnected(false);
@@ -105,48 +123,80 @@ const App = () => {
};
const handleSideBarHiding = () => {
setIsSideBarHidden((prevState) => !prevState);
setIsSideBarHidden((prevState) => {
const newState = !prevState;
if (newState) {
setTimeout(() => {
// Add a delay to ensure layout settles before resize action
fitAddon.current.fit();
if (socket.current && socket.current.readyState === WebSocket.OPEN) {
socket.current.send(JSON.stringify({
cols: terminal.current.cols,
rows: terminal.current.rows,
}));
}
}, 100); // Delay of 100 milliseconds
} else {
setTimeout(() => {
// Refit terminal when showing sidebar as well
fitAddon.current.fit();
if (socket.current && socket.current.readyState === WebSocket.OPEN) {
socket.current.send(JSON.stringify({
cols: terminal.current.cols,
rows: terminal.current.rows,
}));
}
}, 100); // Delay of 100 milliseconds
}
return newState;
});
};
return (
<div className="app-container">
<div className="main-content">
<div className={`sidebar ${isSideBarHidden ? 'hidden' : ''}`}>
<h2>Connection Details</h2>
<input
type="text"
placeholder="Host"
value={host}
onChange={(e) => handleInputChange(e, setHost)}
/>
<input
type="text"
placeholder="Username"
value={username}
onChange={(e) => handleInputChange(e, setUsername)}
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => handleInputChange(e, setPassword)}
/>
<button onClick={handleConnect} disabled={isConnected}>
{isConnected ? 'Connected' : 'Start Session'}
</button>
<div className="app-container">
<div className="main-content">
<div className={`sidebar ${isSideBarHidden ? 'hidden' : ''}`}>
<h2>Connection Details</h2>
<input
type="text"
placeholder="Host"
value={host}
onChange={(e) => handleInputChange(e, setHost)}
/>
<input
type="text"
placeholder="Port"
value={port}
onChange={(e) => handleInputChange(e, setPort)}
/>
<input
type="text"
placeholder="Username"
value={username}
onChange={(e) => handleInputChange(e, setUsername)}
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => handleInputChange(e, setPassword)}
/>
<button onClick={handleConnect} disabled={isConnected}>
{isConnected ? 'Connected' : 'Start Session'}
</button>
</div>
<div ref={terminalRef} className="terminal-container"></div>
</div>
<div ref={terminalRef} className="terminal-container"></div>
{/* Hide button always positioned in the bottom-right corner */}
<button
className="hide-sidebar-button"
onClick={handleSideBarHiding}
>
{isSideBarHidden ? '+' : '-'}
</button>
</div>
{/* Hide button always positioned in the bottom-right corner */}
<button
className="hide-sidebar-button"
onClick={handleSideBarHiding}
>
{isSideBarHidden ? '+' : '-'}
</button>
</div>
);
};

View File

@@ -5,7 +5,7 @@ body {
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow: hidden; /* Prevent scrolling */
overflow: hidden;
}
code {

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 49 KiB