1
0
Fork 0

Switch to xterm
continuous-integration/drone/push Build is passing Details

pull/6/head
Ambrose Chua 2018-12-08 21:58:09 +08:00
parent 11e47f7c35
commit 1a0f7aba91
7 changed files with 83 additions and 19735 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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;
}
});
});
});

View File

@ -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();
});
});

View File

@ -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"
}
}

View File

@ -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" />

View File

@ -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>

View File

@ -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"