added help menu.
This commit is contained in:
parent
82b6670838
commit
9e33737e67
27
main.go
27
main.go
|
@ -5,6 +5,7 @@ import (
|
||||||
// "bufio"
|
// "bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"embed"
|
"embed"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
|
@ -509,6 +510,7 @@ func terminalHandler (w http.ResponseWriter, r *http.Request) {
|
||||||
defer ws.Close()
|
defer ws.Close()
|
||||||
|
|
||||||
cmd := exec.Command("bash")
|
cmd := exec.Command("bash")
|
||||||
|
cmd.Env = append(os.Environ(), "TERM=xterm-256color")
|
||||||
ptmx, err := pty.Start(cmd)
|
ptmx, err := pty.Start(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error starting PTY: %v", err)
|
log.Printf("Error starting PTY: %v", err)
|
||||||
|
@ -537,6 +539,31 @@ func terminalHandler (w http.ResponseWriter, r *http.Request) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the message is binary and starts with control prefix 0xFF.
|
||||||
|
if len(message) > 0 && message[0] == 0xFF {
|
||||||
|
var resizeMsg struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Cols uint16 `json:"cols"`
|
||||||
|
Rows uint16 `json:"rows"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(message[1:], &resizeMsg); err == nil && resizeMsg.Type == "resize" {
|
||||||
|
err := pty.Setsize(ptmx, &pty.Winsize{
|
||||||
|
Cols: resizeMsg.Cols,
|
||||||
|
Rows: resizeMsg.Rows,
|
||||||
|
X: 0,
|
||||||
|
Y: 0,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error resizing PTY: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Printf("Resized PTY to cols: %d, rows: %d", resizeMsg.Cols, resizeMsg.Rows)
|
||||||
|
}
|
||||||
|
continue // Do not write this message to the PTY.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, treat it as normal input.
|
||||||
if _, err := ptmx.Write(message); err != nil {
|
if _, err := ptmx.Write(message); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
const helpMsg = `
|
||||||
|
This is a non interactive Webshell including some additional features to ease
|
||||||
|
communications between server and client.
|
||||||
|
Available Commands:
|
||||||
|
upload Upload files to the server through the file selector of the browser.
|
||||||
|
download <file> Download files from the server to your local download directory.
|
||||||
|
theme <theme> Change the colorscheme of the shell. Type theme to get an overview of all colorschemes.
|
||||||
|
start-interactive Opens a bash shell in an interactive terminal. Type ctrl+d to exit the interactive shell.
|
||||||
|
`
|
||||||
|
// const helpMsg = 'This is a non interactive Webshell including some additional features to ease communications between server and client.\n Available Commands:\n upload\t\t\t\tUpload files to the server through the file selector of the browser.\n download <file>\t\t\tDownload files from the server to your local download directory.\n theme <theme>\t\t\tChange the colorscheme of the shell. Type theme to get an overview of all colorschemes.\n start-interactive\t\t\tOpens a bash shell in an interactive terminal. Type ctrl+d to exi the interactive shell.'
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
const input = document.getElementById("command-input");
|
||||||
|
const terminal = document.getElementById("terminal");
|
||||||
|
|
||||||
|
input.addEventListener("keydown", function(event) {
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
const command = input.value.trim();
|
||||||
|
if (command.startsWith("help")) {
|
||||||
|
event.preventDefault();
|
||||||
|
addLogEntry(helpMsg, 'info');
|
||||||
|
input.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function addLogEntry(message, type) {
|
||||||
|
const logEntry = document.createElement("div");
|
||||||
|
logEntry.classList.add(type === 'error' ? 'error' : 'info');
|
||||||
|
logEntry.textContent = message;
|
||||||
|
terminal.appendChild(logEntry);
|
||||||
|
terminal.scrollTop = terminal.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
|
@ -6,7 +6,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||||
let interactiveMode = false;
|
let interactiveMode = false;
|
||||||
const ansi_up = new AnsiUp;
|
const ansi_up = new AnsiUp;
|
||||||
|
|
||||||
// Listen for the "start-interactive" command in normal mode.
|
|
||||||
input.addEventListener("keydown", function (event) {
|
input.addEventListener("keydown", function (event) {
|
||||||
if (!interactiveMode && event.key === "Enter") {
|
if (!interactiveMode && event.key === "Enter") {
|
||||||
const command = input.value.trim();
|
const command = input.value.trim();
|
||||||
|
@ -21,6 +20,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
function startInteractiveSession() {
|
function startInteractiveSession() {
|
||||||
|
interactiveMode = true;
|
||||||
// Hide the normal terminal and input.
|
// Hide the normal terminal and input.
|
||||||
normalTerminal.style.display = "none";
|
normalTerminal.style.display = "none";
|
||||||
input.style.display = "none";
|
input.style.display = "none";
|
||||||
|
@ -28,15 +28,19 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||||
// Create a new container for xterm.js.
|
// Create a new container for xterm.js.
|
||||||
const xtermContainer = document.createElement("div");
|
const xtermContainer = document.createElement("div");
|
||||||
xtermContainer.id = "xterm-container";
|
xtermContainer.id = "xterm-container";
|
||||||
xtermContainer.style.width = "100%";
|
xtermContainer.style.position = "fixed";
|
||||||
xtermContainer.style.height = "100vh";
|
xtermContainer.style.top = "0";
|
||||||
// Optionally set additional styling (e.g. background color).
|
xtermContainer.style.left = "0";
|
||||||
|
xtermContainer.style.width = window.innerWidth + "px";
|
||||||
|
xtermContainer.style.height = window.innerHeight + "px";
|
||||||
|
xtermContainer.style.zIndex = "1000";
|
||||||
document.body.appendChild(xtermContainer);
|
document.body.appendChild(xtermContainer);
|
||||||
|
|
||||||
// Initialize xterm.js Terminal.
|
|
||||||
const term = new Terminal({
|
const term = new Terminal({
|
||||||
cursorBlink: true,
|
cursorBlink: true,
|
||||||
|
cursorStyle: 'block',
|
||||||
scrollback: 1000,
|
scrollback: 1000,
|
||||||
|
fontSize: 18,
|
||||||
theme: {
|
theme: {
|
||||||
background: "#222",
|
background: "#222",
|
||||||
foreground: "#eee"
|
foreground: "#eee"
|
||||||
|
@ -45,13 +49,19 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||||
const fitAddon = new FitAddon.FitAddon();
|
const fitAddon = new FitAddon.FitAddon();
|
||||||
term.loadAddon(fitAddon);
|
term.loadAddon(fitAddon);
|
||||||
term.open(xtermContainer);
|
term.open(xtermContainer);
|
||||||
|
setTimeout(() => {
|
||||||
fitAddon.fit();
|
fitAddon.fit();
|
||||||
term.focus();
|
term.focus();
|
||||||
|
console.log("Initial fit: container width =", xtermContainer.offsetWidth, "cols =", term.cols);
|
||||||
|
}, 100);
|
||||||
|
|
||||||
// Establish the WebSocket connection.
|
|
||||||
interactiveWS = new WebSocket("ws://" + location.host + "/terminal");
|
interactiveWS = new WebSocket("ws://" + location.host + "/terminal");
|
||||||
interactiveWS.binaryType = "arraybuffer";
|
interactiveWS.binaryType = "arraybuffer";
|
||||||
|
|
||||||
|
interactiveWS.onopen = function () {
|
||||||
|
sendResize();
|
||||||
|
};
|
||||||
|
|
||||||
interactiveWS.onmessage = function (event) {
|
interactiveWS.onmessage = function (event) {
|
||||||
const text = new TextDecoder("utf-8").decode(event.data);
|
const text = new TextDecoder("utf-8").decode(event.data);
|
||||||
term.write(text);
|
term.write(text);
|
||||||
|
@ -60,11 +70,10 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||||
interactiveWS.onclose = function () {
|
interactiveWS.onclose = function () {
|
||||||
interactiveMode = false;
|
interactiveMode = false;
|
||||||
term.write("\r\n--- Interactive session ended ---\r\n");
|
term.write("\r\n--- Interactive session ended ---\r\n");
|
||||||
// Remove the xterm container.
|
|
||||||
if (xtermContainer.parentNode) {
|
if (xtermContainer.parentNode) {
|
||||||
xtermContainer.parentNode.removeChild(xtermContainer);
|
xtermContainer.parentNode.removeChild(xtermContainer);
|
||||||
}
|
}
|
||||||
// Restore the normal terminal UI.
|
window.removeEventListener("resize", handleResize);
|
||||||
normalTerminal.style.display = "block";
|
normalTerminal.style.display = "block";
|
||||||
input.style.display = "block";
|
input.style.display = "block";
|
||||||
input.focus();
|
input.focus();
|
||||||
|
@ -77,13 +86,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||||
if (xtermContainer.parentNode) {
|
if (xtermContainer.parentNode) {
|
||||||
xtermContainer.parentNode.removeChild(xtermContainer);
|
xtermContainer.parentNode.removeChild(xtermContainer);
|
||||||
}
|
}
|
||||||
|
window.removeEventListener("resize", handleResize);
|
||||||
normalTerminal.style.display = "block";
|
normalTerminal.style.display = "block";
|
||||||
input.style.display = "block";
|
input.style.display = "block";
|
||||||
};
|
};
|
||||||
|
|
||||||
// When the user types in xterm, send the data to the server.
|
|
||||||
term.onData(function (data) {
|
term.onData(function (data) {
|
||||||
// If the user presses Ctrl+D (ASCII 4), exit the session.
|
|
||||||
if (data === "\x04") {
|
if (data === "\x04") {
|
||||||
term.write("\r\n--- Exiting interactive session ---\r\n");
|
term.write("\r\n--- Exiting interactive session ---\r\n");
|
||||||
interactiveWS.close();
|
interactiveWS.close();
|
||||||
|
@ -92,6 +100,33 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
interactiveMode = true;
|
function handleResize() {
|
||||||
|
const newWidth = window.innerWidth;
|
||||||
|
const newHeight = window.innerHeight;
|
||||||
|
xtermContainer.style.width = newWidth + "px";
|
||||||
|
xtermContainer.style.height = newHeight + "px";
|
||||||
|
console.log("Resizing: new width =", newWidth, "new height =", newHeight);
|
||||||
|
fitAddon.fit();
|
||||||
|
sendResize();
|
||||||
|
}
|
||||||
|
window.addEventListener("resize", handleResize);
|
||||||
|
|
||||||
|
// Send a resize message using a custom control prefix (0xFF).
|
||||||
|
function sendResize() {
|
||||||
|
const resizeData = {
|
||||||
|
type: "resize",
|
||||||
|
cols: term.cols,
|
||||||
|
rows: term.rows
|
||||||
|
};
|
||||||
|
const jsonStr = JSON.stringify(resizeData);
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const jsonBuffer = encoder.encode(jsonStr);
|
||||||
|
// Create a Uint8Array with one extra byte for the prefix.
|
||||||
|
const buffer = new Uint8Array(jsonBuffer.length + 1);
|
||||||
|
buffer[0] = 0xFF; // Control prefix.
|
||||||
|
buffer.set(jsonBuffer, 1);
|
||||||
|
interactiveWS.send(buffer.buffer);
|
||||||
|
console.log("Sent resize: cols =", term.cols, "rows =", term.rows);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,14 +8,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Default is pwny theme */
|
/* Default is pwny theme */
|
||||||
body {
|
html, body {
|
||||||
background-color: var(--bg-color);
|
background-color: var(--bg-color);
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
font-size: 14pt;
|
font-size: 14pt;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
height: 100vh;
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
@ -80,6 +81,25 @@ span.ps1 {
|
||||||
color: var(--ps1-color);
|
color: var(--ps1-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.info {
|
||||||
|
/* text-align: justify; */
|
||||||
|
/* color: var(--text-color); */
|
||||||
|
/* animation: 0.75s 2 changeColor; */
|
||||||
|
animation: changeColor 1.75s forwards;
|
||||||
|
/* max-width: 80ch; */
|
||||||
|
}
|
||||||
|
@keyframes changeColor {
|
||||||
|
from { color: var(--error-color) };
|
||||||
|
to { color: var(--text-color) };
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
/* text-align: justify; */
|
||||||
|
color: var(--error-color);
|
||||||
|
/* max-width: 80ch; */
|
||||||
|
}
|
||||||
|
|
||||||
.light-theme {
|
.light-theme {
|
||||||
--bg-color: #fff;
|
--bg-color: #fff;
|
||||||
--text-color: #222;
|
--text-color: #222;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
<title>Go Web Shell</title>
|
<title>Go Web Shell</title>
|
||||||
<script type="text/javascript" src="static/keyboard-shortcuts.js"></script>
|
<script type="text/javascript" src="static/keyboard-shortcuts.js"></script>
|
||||||
<script type="text/javascript" src="static/download-command.js"></script>
|
<script type="text/javascript" src="static/download-command.js"></script>
|
||||||
|
<script type="text/javascript" src="static/help-command.js"></script>
|
||||||
<script type="text/javascript" src="static/switch-themes.js"></script>
|
<script type="text/javascript" src="static/switch-themes.js"></script>
|
||||||
<script type="text/javascript" src="static/start-interactive.js"></script>
|
<script type="text/javascript" src="static/start-interactive.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/ansi_up@5.0.0/ansi_up.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/ansi_up@5.0.0/ansi_up.min.js"></script>
|
||||||
|
|
Loading…
Reference in New Issue