Switch to xterm
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
11e47f7c35
commit
1a0f7aba91
19633
assets/hterm_all.js
19633
assets/hterm_all.js
File diff suppressed because it is too large
Load Diff
128
assets/shell.js
128
assets/shell.js
|
@ -5,94 +5,52 @@ $(document).ready(() => {
|
|||
if ($shell.length < 1) {
|
||||
return;
|
||||
}
|
||||
let $close = $(".shell-close");
|
||||
let $close = $("#shell-close");
|
||||
|
||||
let closeTimeout = null;
|
||||
Terminal.applyAddon(attach);
|
||||
Terminal.applyAddon(fit);
|
||||
|
||||
// excerpt from wetty
|
||||
let term = new Terminal();
|
||||
let ws = new WebSocket("ws" + (window.location.protocol === "https:" ? "s" : "") + "://" + window.location.host + "/websocket?path=" + encodeURIComponent($shell.data("path")));
|
||||
term.attach(ws, true, true);
|
||||
term.open($shell[0]);
|
||||
|
||||
let socket = io("?path=" + encodeURIComponent($shell.data("path")));
|
||||
let term = null;
|
||||
let buf = "";
|
||||
ws.addEventListener("open", () => {
|
||||
|
||||
function Wetty(argv) {
|
||||
this.argv_ = argv;
|
||||
this.io = null;
|
||||
this.pid_ = -1;
|
||||
}
|
||||
Wetty.prototype.run = function () {
|
||||
this.io = this.argv_.io.push();
|
||||
|
||||
this.io.onVTKeystroke = this.sendString_.bind(this);
|
||||
this.io.sendString = this.sendString_.bind(this);
|
||||
this.io.onTerminalResize = this.onTerminalResize.bind(this);
|
||||
};
|
||||
Wetty.prototype.sendString_ = function (str) {
|
||||
socket.emit("input", str);
|
||||
|
||||
// cancel close timeout
|
||||
if (closeTimeout != null) {
|
||||
clearTimeout(closeTimeout);
|
||||
term.io.writeUTF16("\r\nkeyboard input detected. timeout canceled");
|
||||
closeTimeout = null;
|
||||
}
|
||||
};
|
||||
Wetty.prototype.onTerminalResize = function (col, row) {
|
||||
socket.emit("resize", { col: col, row: row });
|
||||
};
|
||||
|
||||
socket.on("connect", () => {
|
||||
console.log("socket.io connection established");
|
||||
lib.init(() => {
|
||||
hterm.defaultStorage = new lib.Storage.Local();
|
||||
term = new hterm.Terminal();
|
||||
window.term = term;
|
||||
term.decorate(document.getElementById("shell"));
|
||||
// force custom size
|
||||
$shell.find("iframe").css("height", "calc(100% - 56px * 2)");
|
||||
|
||||
term.setCursorPosition(0, 0);
|
||||
term.setCursorVisible(true);
|
||||
term.prefs_.set("ctrl-c-copy", true);
|
||||
term.prefs_.set("ctrl-v-paste", true);
|
||||
term.prefs_.set("use-default-window-copy", true);
|
||||
|
||||
term.runCommandClass(Wetty);
|
||||
socket.emit("resize", {
|
||||
col: term.screenSize.width,
|
||||
row: term.screenSize.height,
|
||||
});
|
||||
|
||||
if (buf && buf != "") {
|
||||
term.io.writeUTF16(buf);
|
||||
buf = "";
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
socket.on("output", (data) => {
|
||||
if (!term) {
|
||||
buf += data;
|
||||
return;
|
||||
}
|
||||
term.io.writeUTF16(data);
|
||||
});
|
||||
|
||||
socket.on("disconnect", () => {
|
||||
console.log("socket.io connection closed");
|
||||
});
|
||||
|
||||
// end excerpt from wetty
|
||||
|
||||
socket.on("disconnect", () => {
|
||||
term.io.writeUTF16("\r\n\r\nclosing shell in 2 seconds...");
|
||||
closeTimeout = setTimeout(() => {
|
||||
window.location.pathname = window.location.pathname.replace("@shell", "");
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
$close.click(() => {
|
||||
socket.disconnect();
|
||||
});
|
||||
// resize
|
||||
term.on("resize", ({ cols, rows }) => {
|
||||
console.log(cols, rows);
|
||||
const buf = Uint16Array.of(0, cols, rows);
|
||||
ws.send(buf);
|
||||
});
|
||||
$(window).on("resize", () => {
|
||||
term.fit();
|
||||
});
|
||||
term.fit();
|
||||
|
||||
// close
|
||||
let closeTimeout = null;
|
||||
$close.on("click", (e) => {
|
||||
e.preventDefault();
|
||||
if (ws.readyState !== 1) {
|
||||
window.location.pathname = window.location.pathname.replace("@shell", "");
|
||||
} else {
|
||||
ws.close();
|
||||
}
|
||||
});
|
||||
ws.addEventListener("close", () => {
|
||||
term.write("\r\n\r\nclosing shell in 2 seconds...");
|
||||
closeTimeout = setTimeout(() => {
|
||||
window.location.pathname = window.location.pathname.replace("@shell", "");
|
||||
}, 2000);
|
||||
});
|
||||
term.on("data", () => {
|
||||
if (closeTimeout != null) {
|
||||
clearTimeout(closeTimeout);
|
||||
term.write("\r\nkeyboard input detected. timeout canceled");
|
||||
closeTimeout = null;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
31
index.js
31
index.js
|
@ -10,6 +10,7 @@ const bodyparser = require("body-parser");
|
|||
const session = require("express-session");
|
||||
const busboy = require("connect-busboy");
|
||||
const flash = require("connect-flash");
|
||||
const querystring = require("querystring");
|
||||
|
||||
const archiver = require("archiver");
|
||||
|
||||
|
@ -64,6 +65,7 @@ app.use("/bootstrap", express.static(path.join(__dirname, "node_modules/bootstra
|
|||
app.use("/octicons", express.static(path.join(__dirname, "node_modules/octicons/build")));
|
||||
app.use("/jquery", express.static(path.join(__dirname, "node_modules/jquery/dist")));
|
||||
app.use("/filesize", express.static(path.join(__dirname, "node_modules/filesize/lib")));
|
||||
app.use("/xterm", express.static(path.join(__dirname, "node_modules/xterm/dist")));
|
||||
app.use("/assets", express.static(path.join(__dirname, "assets")));
|
||||
|
||||
app.use(session({
|
||||
|
@ -431,7 +433,7 @@ if (shellable || cmdable) {
|
|||
});
|
||||
|
||||
const pty = require("node-pty");
|
||||
const io = require("socket.io")(http);
|
||||
const WebSocket = require("ws");
|
||||
|
||||
app.get("/*@shell", (req, res) => {
|
||||
res.filename = req.params[0];
|
||||
|
@ -441,9 +443,11 @@ if (shellable || cmdable) {
|
|||
}));
|
||||
});
|
||||
|
||||
io.on("connection", (socket) => {
|
||||
let cwd = relative(socket.handshake.query.path);
|
||||
|
||||
const ws = new WebSocket.Server({ server: http });
|
||||
ws.on("connection", (socket, request) => {
|
||||
console.log(request.url);
|
||||
const { path } = querystring.parse(request.url.split("?")[1]);
|
||||
let cwd = relative(path);
|
||||
let term = pty.spawn(exec, args, {
|
||||
name: "xterm-256color",
|
||||
cols: 80,
|
||||
|
@ -453,19 +457,24 @@ if (shellable || cmdable) {
|
|||
console.log("pid " + term.pid + " shell " + process.env.SHELL + " started in " + cwd);
|
||||
|
||||
term.on("data", (data) => {
|
||||
socket.emit("output", data);
|
||||
socket.send(data, { binary: true });
|
||||
});
|
||||
term.on("exit", (code) => {
|
||||
console.log("pid " + term.pid + " ended")
|
||||
socket.disconnect();
|
||||
socket.close();
|
||||
});
|
||||
socket.on("resize", (data) => {
|
||||
term.resize(data.col, data.row);
|
||||
});
|
||||
socket.on("input", (data) => {
|
||||
socket.on("message", (data) => {
|
||||
// special messages should decode to Buffers
|
||||
if (Buffer.isBuffer(data)) {
|
||||
switch (data.readUInt16BE(0)) {
|
||||
case 0:
|
||||
term.resize(data.readUInt16BE(1), data.readUInt16BE(2));
|
||||
return;
|
||||
}
|
||||
}
|
||||
term.write(data);
|
||||
});
|
||||
socket.on("disconnect", () => {
|
||||
socket.on("close", () => {
|
||||
term.end();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"octicons": "^6.0.1",
|
||||
"rimraf": "^2.6.2",
|
||||
"socket.io": "^2.0.4",
|
||||
"thirty-two": "^1.0.2"
|
||||
"thirty-two": "^1.0.2",
|
||||
"ws": "^6.1.2",
|
||||
"xterm": "^3.8.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,12 @@
|
|||
<script src="/jquery/jquery.min.js"></script>
|
||||
<link rel="stylesheet" href="/bootstrap/css/bootstrap.min.css" />
|
||||
<script src="/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
<link rel="stylesheet" href="/xterm/xterm.css" />
|
||||
<script src="/xterm/xterm.js"></script>
|
||||
<script src="/xterm/addons/attach/attach.js"></script>
|
||||
<script src="/xterm/addons/fit/fit.js"></script>
|
||||
|
||||
<script src="/filesize/filesize.js"></script>
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<script src="/assets/hterm_all.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/assets/fonts.css" />
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{{> navbar}}
|
||||
|
||||
<div style="padding-top: 56px; padding-bottom: 56px;">
|
||||
<main id="shell" data-path="/{{path}}"></main>
|
||||
<div style="padding-top: 56px; padding-bottom: 56px; height: 100vh">
|
||||
<main id="shell" style="height: 100%; background: #000" data-path="/{{path}}"></main>
|
||||
</div>
|
||||
|
||||
<nav class="navbar navbar-light bg-light fixed-bottom p-1 px-2">
|
||||
<div class="btn-group m-1" role="group">
|
||||
<a class="btn btn-danger" href="/{{path}}" class="shell-close" data-placement="top" title="Close and return to folder">
|
||||
<a class="btn btn-danger" href="/{{path}}" id="shell-close" data-placement="top" title="Close and return to folder">
|
||||
{{octicon "chevron-left"}}
|
||||
<span class="d-none d-sm-inline">Close shell</span>
|
||||
</a>
|
||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -981,6 +981,12 @@ wrappy@1:
|
|||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
|
||||
ws@^6.1.2:
|
||||
version "6.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.2.tgz#3cc7462e98792f0ac679424148903ded3b9c3ad8"
|
||||
dependencies:
|
||||
async-limiter "~1.0.0"
|
||||
|
||||
ws@~3.3.1:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2"
|
||||
|
@ -997,6 +1003,10 @@ xtend@^4.0.0:
|
|||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
|
||||
|
||||
xterm@^3.8.0:
|
||||
version "3.8.0"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.8.0.tgz#55d1de518bdc9c9793823f5e4e97d6898972938d"
|
||||
|
||||
yargs@~3.10.0:
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"
|
||||
|
|
Loading…
Reference in New Issue