fixed scrolling and buffer times- oh yeah also huge backend overhual
This commit is contained in:
parent
1eeb632ff2
commit
524969370d
|
@ -1,11 +1,22 @@
|
||||||
var buffer = "";
|
var recv_buffer = [];
|
||||||
var send_buffer = "";
|
var send_buffer = [];
|
||||||
|
|
||||||
var content;
|
var content;
|
||||||
var mobile_input;
|
var mobile_input;
|
||||||
|
|
||||||
var client;
|
var client;
|
||||||
|
|
||||||
|
var my_colour = false;
|
||||||
var pre_buffer_chars = 0;
|
var pre_buffer_chars = 0;
|
||||||
var term_interval = 10;
|
|
||||||
var ready = false;
|
const DATA_TYPES = {
|
||||||
|
text: 0,
|
||||||
|
colour: 1,
|
||||||
|
buffer: 2,
|
||||||
|
backspace: 3,
|
||||||
|
backword: 4,
|
||||||
|
arrow: 5,
|
||||||
|
};
|
||||||
|
|
||||||
function start() {
|
function start() {
|
||||||
console.log("%chello, world!", "color: #b7fd49; font-size: 3rem; font-weight: bold");
|
console.log("%chello, world!", "color: #b7fd49; font-size: 3rem; font-weight: bold");
|
||||||
|
@ -34,74 +45,115 @@ to help you feel a little more comfortable, i've prepared some commands for you:
|
||||||
content = document.getElementById("content");
|
content = document.getElementById("content");
|
||||||
mobile_input = document.getElementById("mobile-input");
|
mobile_input = document.getElementById("mobile-input");
|
||||||
|
|
||||||
content.addEventListener("click", () => {
|
content.addEventListener("touchend", () => {
|
||||||
mobile_input.focus();
|
mobile_input.focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
buffer += "Connecting to the server...";
|
add_system_message("Connecting to the server...");
|
||||||
|
|
||||||
setTimeout(connect, 500);
|
setTimeout(connect, 500);
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
if (send_buffer.length > 0) {
|
||||||
|
const data = JSON.stringify(send_buffer[0]);
|
||||||
|
client.send(data);
|
||||||
|
send_buffer = send_buffer.slice(1);
|
||||||
|
}
|
||||||
|
}, 1000 / 600);
|
||||||
|
|
||||||
loop();
|
loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
function loop() {
|
function loop() {
|
||||||
if (buffer.length > 0) {
|
|
||||||
const char = buffer.substring(0, 1);
|
|
||||||
insert_text(char);
|
|
||||||
buffer = buffer.substring(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (send_buffer.length > 0) {
|
|
||||||
const char = send_buffer.substring(0, 1);
|
|
||||||
client.send(char);
|
|
||||||
send_buffer = send_buffer.substring(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
mobile_input.value = content.innerText;
|
mobile_input.value = content.innerText;
|
||||||
|
|
||||||
setTimeout(loop, term_interval);
|
setTimeout(loop, 1000 / 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
function connect() {
|
function connect() {
|
||||||
client = new WebSocket("wss://" + window.location.host);
|
client = new WebSocket("wss://" + window.location.host);
|
||||||
|
|
||||||
client.addEventListener('open', () => {
|
client.addEventListener('open', () => {
|
||||||
// insert_text('\x00');
|
add_system_message(`\nConnection successful.\n\n`);
|
||||||
buffer += "\nConnection successful.\n\n";
|
add_system_message(`=== BEGIN SESSION ===\n\n`);
|
||||||
buffer += "=== BEGIN SESSION ===\n\n";
|
new_caret();
|
||||||
});
|
});
|
||||||
|
|
||||||
client.addEventListener('message', event => {
|
client.addEventListener('message', event => { handle_message(JSON.parse(event.data)) });
|
||||||
buffer += event.data;
|
|
||||||
if (pre_buffer_chars == 0) {
|
|
||||||
pre_buffer_chars = content.innerText.length + buffer.length;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
client.addEventListener('close', () => {
|
client.addEventListener('close', () => {
|
||||||
insert_text("\n\n[CONNECTION LOST, PLEASE REFRESH]");
|
add_system_message(`\n[CONNECTION LOST, PLEASE REFRESH]\n`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function insert_text(text) {
|
function add_system_message(text) {
|
||||||
const carat = content.querySelector("#carat");
|
const span = document.createElement("span");
|
||||||
if (carat) carat.remove();
|
span.classList.add('sticky');
|
||||||
|
span.innerText = text;
|
||||||
|
content.appendChild(span);
|
||||||
|
|
||||||
if (text == "\x00") {
|
new_caret();
|
||||||
content.innerText = "";
|
}
|
||||||
pre_buffer_chars = 0;
|
|
||||||
} else if (text == "\b") {
|
function handle_message(data) {
|
||||||
if (content.innerText.length > pre_buffer_chars) {
|
if (!data.type && data.type != 0) return;
|
||||||
|
|
||||||
|
const is_at_bottom = content.scrollTop == content.scrollTopMax;
|
||||||
|
|
||||||
|
switch (data.type) {
|
||||||
|
case DATA_TYPES.colour:
|
||||||
|
my_colour = data.colour;
|
||||||
|
console.log(`%cColour has been changed to ${my_colour}`, `color: ${my_colour}`);
|
||||||
|
break;
|
||||||
|
case DATA_TYPES.backspace:
|
||||||
|
content.querySelectorAll("#caret").forEach(caret => caret.remove());
|
||||||
|
/*
|
||||||
|
const last_child = content.lastChild;
|
||||||
|
if (last_child.classList.contains('sticky')) break;
|
||||||
|
last_child.remove();
|
||||||
|
*/
|
||||||
|
if (content.innerText.length <= pre_buffer_chars) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
content.innerText = content.innerText.slice(0, content.innerText.length - 1);
|
content.innerText = content.innerText.slice(0, content.innerText.length - 1);
|
||||||
}
|
break;
|
||||||
} else {
|
case DATA_TYPES.text:
|
||||||
content.innerText += text;
|
/*
|
||||||
|
const span = document.createElement("span");
|
||||||
|
if (data.colour) span.style.color = data.colour;
|
||||||
|
if (data.sticky) span.classList.add('sticky');
|
||||||
|
span.innerText = data.text;
|
||||||
|
content.appendChild(span);
|
||||||
|
*/
|
||||||
|
content.innerText += data.text;
|
||||||
|
break;
|
||||||
|
case DATA_TYPES.buffer:
|
||||||
|
content.innerText += data.data;
|
||||||
|
break;
|
||||||
|
/*
|
||||||
|
data.data.forEach(block => {
|
||||||
|
handle_message(block);
|
||||||
|
});
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
const new_carat = document.createElement("div");
|
if (pre_buffer_chars == 0) {
|
||||||
new_carat.id = "carat";
|
pre_buffer_chars = content.innerText.length;
|
||||||
content.appendChild(new_carat);
|
}
|
||||||
|
|
||||||
|
new_caret();
|
||||||
|
|
||||||
|
if (is_at_bottom) content.scrollTop = content.scrollTopMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
function new_caret() {
|
||||||
|
content.querySelectorAll("#caret").forEach(caret => caret.remove());
|
||||||
|
const new_caret = document.createElement("div");
|
||||||
|
new_caret.id = "caret";
|
||||||
|
if (my_colour) {
|
||||||
|
new_caret.style.backgroundColor = my_colour;
|
||||||
|
}
|
||||||
|
content.appendChild(new_caret);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_input(event) {
|
function handle_input(event) {
|
||||||
|
@ -109,38 +161,77 @@ function handle_input(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key == "Backspace") {
|
switch (event.key) {
|
||||||
if (event.ctrlKey && send_buffer.length == 0) {
|
case "Backspace":
|
||||||
const last_space = content.innerText.lastIndexOf(" ");
|
if (event.ctrlKey) {
|
||||||
|
if (send_buffer.length > 0) return;
|
||||||
|
/*
|
||||||
|
send_buffer.push({
|
||||||
|
type: DATA_TYPES.backword,
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
var break_point = content.innerText.lastIndexOf(" ");
|
||||||
const last_newline = content.innerText.lastIndexOf("\n");
|
const last_newline = content.innerText.lastIndexOf("\n");
|
||||||
|
if (last_newline > break_point) break_point = last_newline;
|
||||||
var break_at = last_space;
|
const count = content.innerText.length - break_point;
|
||||||
if (last_newline > last_space) {
|
for (var i = 0; i < count; i++) {
|
||||||
break_at = last_newline;
|
send_buffer.push({
|
||||||
|
type: DATA_TYPES.backspace,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
send_buffer.push({
|
||||||
|
type: DATA_TYPES.backspace,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
case "Enter":
|
||||||
|
send_buffer.push({
|
||||||
|
type: DATA_TYPES.text,
|
||||||
|
text: "\n",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
case "ArrowUp":
|
||||||
|
send_buffer.push({
|
||||||
|
type: DATA_TYPES.arrow,
|
||||||
|
dir: "up",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
case "ArrowDown":
|
||||||
|
send_buffer.push({
|
||||||
|
type: DATA_TYPES.arrow,
|
||||||
|
dir: "down",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
case "ArrowLeft":
|
||||||
|
send_buffer.push({
|
||||||
|
type: DATA_TYPES.arrow,
|
||||||
|
dir: "left",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
case "ArrowRight":
|
||||||
|
send_buffer.push({
|
||||||
|
type: DATA_TYPES.arrow,
|
||||||
|
dir: "right",
|
||||||
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const word_length = content.innerText.length - break_at;
|
|
||||||
for (let i = 0; i < word_length; i++) {
|
|
||||||
send_buffer += '\b';
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
send_buffer += '\b';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (event.key == "Enter") {
|
|
||||||
send_buffer += '\n';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (event.key.length > 1) {
|
if (event.key.length > 1) {
|
||||||
|
// server will discard text over 1 character, anyway
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.ctrlKey) {
|
if (event.ctrlKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
send_buffer += event.key;
|
send_buffer.push({
|
||||||
content.scrollTop = content.scrollHeight;
|
type: DATA_TYPES.text,
|
||||||
|
text: event.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
content.scrollTop = content.scrollTopMax;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_paste(event) {
|
function handle_paste(event) {
|
||||||
|
@ -151,8 +242,11 @@ function handle_paste(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const paste = (event.clipboardData || window.clipboardData).getData("text");
|
const paste = (event.clipboardData || window.clipboardData).getData("text");
|
||||||
send_buffer += paste;
|
send_buffer.push({
|
||||||
content.scrollTop = content.scrollHeight;
|
type: DATA_TYPES.text,
|
||||||
|
text: paste,
|
||||||
|
});
|
||||||
|
content.scrollTop = content.scrollTopMax;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PALETTE = {
|
const PALETTE = {
|
||||||
|
|
|
@ -28,16 +28,16 @@ pre#content {
|
||||||
text-shadow: 0 0 1em, 0 0 3em;
|
text-shadow: 0 0 1em, 0 0 3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#carat {
|
div#caret {
|
||||||
width: .5em;
|
width: .5em;
|
||||||
height: .9em;
|
height: .9em;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background: var(--colour);
|
background: var(--colour);
|
||||||
transform: translateY(1px);
|
transform: translateY(1px);
|
||||||
animation: linear .5s infinite forwards carat-blink;
|
animation: linear .5s infinite forwards caret-blink;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes carat-blink {
|
@keyframes caret-blink {
|
||||||
from {
|
from {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
105
server/main.js
105
server/main.js
|
@ -20,7 +20,16 @@ const MIME_TYPES = {
|
||||||
svg: "image/svg+xml",
|
svg: "image/svg+xml",
|
||||||
};
|
};
|
||||||
|
|
||||||
const motds = [
|
const DATA_TYPES = {
|
||||||
|
text: 0,
|
||||||
|
colour: 1,
|
||||||
|
buffer: 2,
|
||||||
|
backspace: 3,
|
||||||
|
backword: 4,
|
||||||
|
arrow: 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
const MOTDS = [
|
||||||
"hello, world!",
|
"hello, world!",
|
||||||
"all your TTY are belong to us.",
|
"all your TTY are belong to us.",
|
||||||
"TIP: got a linux system low on storage? try running `sudo rm -rf /`!",
|
"TIP: got a linux system low on storage? try running `sudo rm -rf /`!",
|
||||||
|
@ -46,7 +55,7 @@ let sockets = [];
|
||||||
|
|
||||||
let buffer = "";
|
let buffer = "";
|
||||||
const MAX_BUFFER_SIZE = 10240;
|
const MAX_BUFFER_SIZE = 10240;
|
||||||
const MAX_MESSAGE_LENGTH = 64;
|
const MAX_MESSAGE_LENGTH = 1024;
|
||||||
|
|
||||||
async function get_file(url) {
|
async function get_file(url) {
|
||||||
const paths = [STATIC_PATH, url];
|
const paths = [STATIC_PATH, url];
|
||||||
|
@ -76,14 +85,31 @@ const server = https.createServer(config, async (req, res) => {
|
||||||
|
|
||||||
const wss = new Websocket.Server({ server });
|
const wss = new Websocket.Server({ server });
|
||||||
wss.on('connection', socket => {
|
wss.on('connection', socket => {
|
||||||
socket.send(`${banner}/* ${motds[Math.floor(Math.random() * motds.length)]} */\n\n`);
|
/*
|
||||||
socket.send(buffer);
|
socket.colour = generate_colour();
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
type: DATA_TYPES.colour,
|
||||||
|
colour: socket.colour,
|
||||||
|
}));
|
||||||
|
*/
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
type: DATA_TYPES.text,
|
||||||
|
text: `${banner}/* ${MOTDS[Math.floor(Math.random() * MOTDS.length)]} */\n\n`,
|
||||||
|
colour: false,
|
||||||
|
sticky: true,
|
||||||
|
}));
|
||||||
|
if (buffer) {
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
type: DATA_TYPES.buffer,
|
||||||
|
data: buffer,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
sockets.push(socket);
|
sockets.push(socket);
|
||||||
|
|
||||||
// console.log(`new connection.\n\tcurrent connections: ${sockets.length}`);
|
// console.log(`new connection.\n\tcurrent connections: ${sockets.length}`);
|
||||||
|
|
||||||
socket.on('message', handle_message);
|
socket.on('message', event => { handle_message(JSON.parse(event), socket) });
|
||||||
|
|
||||||
socket.on('close', () => {
|
socket.on('close', () => {
|
||||||
sockets = sockets.filter(s => s !== socket);
|
sockets = sockets.filter(s => s !== socket);
|
||||||
|
@ -91,38 +117,69 @@ wss.on('connection', socket => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function handle_message(msg) {
|
function handle_message(data, user) {
|
||||||
if (msg.length > MAX_MESSAGE_LENGTH) {
|
switch (data.type) {
|
||||||
|
case DATA_TYPES.backword:
|
||||||
|
var break_point = buffer.lastIndexOf(" ");
|
||||||
|
const last_newline = buffer.lastIndexOf("\n");
|
||||||
|
if (last_newline > break_point) break_point = last_newline;
|
||||||
|
buffer = buffer.substring(0, break_point);
|
||||||
|
for (var i = 0; i < buffer.length - break_point; i++) {
|
||||||
|
broadcast(JSON.stringify({
|
||||||
|
type: DATA_TYPES.backspace,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
case DATA_TYPES.backspace:
|
||||||
|
buffer = buffer.substring(0, buffer.length - 1);
|
||||||
|
broadcast(JSON.stringify({
|
||||||
|
type: DATA_TYPES.backspace,
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
case DATA_TYPES.text:
|
||||||
|
if (buffer.length >= MAX_BUFFER_SIZE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msg == '\b') {
|
if (data.text.length > MAX_MESSAGE_LENGTH) {
|
||||||
buffer = buffer.slice(0, buffer.length - 1);
|
|
||||||
send_text('\b');
|
|
||||||
return;
|
|
||||||
} else if (buffer.length >= MAX_BUFFER_SIZE) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msg == '\n') {
|
block = {
|
||||||
buffer += '\n';
|
type: DATA_TYPES.text,
|
||||||
send_text('\n');
|
text: data.text,
|
||||||
return;
|
colour: user.colour,
|
||||||
|
};
|
||||||
|
buffer += data.text;
|
||||||
|
broadcast(JSON.stringify(block));
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer += msg.toString();
|
|
||||||
send_text(msg.toString());
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (buffer.length > MAX_BUFFER_SIZE) {
|
if (buffer.length > MAX_BUFFER_SIZE) {
|
||||||
buffer = buffer.slice(buffer.length - MAX_BUFFER_SIZE, buffer.length);
|
send_as_server(`\n\nSERVER: This channel's maximum buffer length has been hit (${MAX_BUFFER_SIZE}).\n` +
|
||||||
|
`You will need to make more room, or the server will have to be restarted.\n` +
|
||||||
|
`Apologies for the inconvenience!`)
|
||||||
}
|
}
|
||||||
*/
|
}
|
||||||
|
|
||||||
|
function generate_colour() {
|
||||||
|
let result = '#';
|
||||||
|
let hexref = '0123456789abcdef';
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
result += hexref.charAt(Math.floor(Math.random() * hexref.length * .75) + 4);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
server.listen(PORT, () => {
|
server.listen(PORT, () => {
|
||||||
console.log(`OpenTerminal is now LIVE on https://127.0.0.1:${PORT}!`);
|
console.log(`OpenTerminal is now LIVE on https://127.0.0.1:${PORT}!`);
|
||||||
});
|
});
|
||||||
|
|
||||||
function send_text(text) {
|
function send_as_server(message) {
|
||||||
sockets.forEach(s => s.send(text));
|
broadcast(JSON.stringify({
|
||||||
|
type: DATA_TYPES.text,
|
||||||
|
text: message,
|
||||||
|
colour: "#ffffff",
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function broadcast(data) {
|
||||||
|
sockets.forEach(s => s.send(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue