diff --git a/.gitignore b/.gitignore index 10f3e1c..332bbc0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +**/.DS_Store node_modules/ certs/ diff --git a/public/index.html b/public/index.html index d4bad3d..6f1f8b7 100644 --- a/public/index.html +++ b/public/index.html @@ -20,7 +20,7 @@ - + diff --git a/public/scripts/main.js b/public/scripts/main.js index 3774fdb..0aecae5 100644 --- a/public/scripts/main.js +++ b/public/scripts/main.js @@ -1,354 +1,8 @@ -var recv_buffer = []; -var send_buffer = []; - -var content; -var mobile_input; - -var client; - -var my_colour = false; -var pre_buffer_chars = 0; -var server_url = ""; - -const DATA_TYPES = { - ping: 0, - text: 1, - colour: 2, - buffer: 3, - backspace: 4, - backword: 5, - arrow: 6, -}; - -function start() { - console.log("%chello, world!", "color: #b7fd49; font-size: 3rem; font-weight: bold"); - console.log( -`welcome to OpenTerminal! -home to an online terminal and communal text buffer. - -i hope you enjoy your stay here! -to help you feel a little more comfortable, i've prepared some commands for you: - -- set_colours(foreground, background) - changes the foreground and background colours of your terminal! - \`foreground\` and \`background\` must be hex colour codes, such as \`#ff00ff\`. - -- set_palette(palette_name) - changes the foreground and background colours of your terminal to one of our many options of premade themes! including but not limited to the entire collection of catppuccin mocha colours! (i really like their palette ;p) - - try it out! enter \`PALETTE\` into your console and browse the list of themes we have!`); - - server_url = new URL(window.location).searchParams.get("server") || window.location.host; - - const foreground = localStorage.getItem("foreground"); - const background = localStorage.getItem("background"); - if (foreground && background) { - set_colours(foreground, background); - } - - if (localStorage.getItem("lcd")) { - document.body.classList.add("lcd"); - } - - content = document.getElementById("content"); - mobile_input = document.getElementById("mobile-input"); - - content.addEventListener("touchend", () => { - mobile_input.focus(); - }); - - add_system_message("Connecting to the server..."); - - 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(); -} - -function loop() { - mobile_input.value = content.innerText; - - setTimeout(loop, 1000 / 60); -} - -function connect() { - client = new WebSocket("wss://" + server_url); - - client.addEventListener('open', () => { - add_system_message(`\nConnection successful.\n\n`); - add_system_message(`=== BEGIN SESSION ===\n\n`); - new_caret(); - }); - - client.addEventListener('message', event => { handle_message(JSON.parse(event.data)) }); - - client.addEventListener('close', () => { - add_system_message(`\n[CONNECTION LOST, PLEASE REFRESH]\n`); - }); -} - -function add_system_message(text) { - const span = document.createElement("span"); - span.classList.add('sticky'); - span.innerText = text; - content.appendChild(span); - - new_caret(); -} - -function handle_message(data) { - if (!data.type && data.type != 0) return; - - const is_at_bottom = content.scrollHeight - content.offsetHeight - content.scrollTop < 10; - - switch (data.type) { - case DATA_TYPES.ping: - client.send(JSON.stringify({type: DATA_TYPES.ping})); - break; - 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); - break; - case DATA_TYPES.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); - }); - */ - } - - if (pre_buffer_chars == 0) { - pre_buffer_chars = content.innerText.length; - } - - new_caret(); - - if (is_at_bottom) content.scrollTop = content.scrollHeight - content.offsetHeight; -} - -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) { - if (event.key == "'") { - event.preventDefault(); - } - - switch (event.key) { - case "Backspace": - 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"); - if (last_newline > break_point) break_point = last_newline; - const count = content.innerText.length - break_point; - for (var i = 0; i < count; i++) { - 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; - } - - if (event.key.length > 1) { - // server will discard text over 1 character, anyway - return; - } - - if (event.ctrlKey || event.metaKey) { - return; - } - - send_buffer.push({ - type: DATA_TYPES.text, - text: event.key, - }); - - content.scrollTop = content.scrollHeight - content.offsetHeight; -} - -function handle_paste(event) { - event.preventDefault(); - - if (send_buffer.length > 0) { - return; - } - - const paste = (event.clipboardData || window.clipboardData).getData("text"); - send_buffer.push({ - type: DATA_TYPES.text, - text: paste, - }); - content.scrollTop = content.scrollHeight - content.offsetHeight; -} - -const PALETTE = { - ari: ["#b7fd49", "#111111"], - mono: ["#ffffff", "#111111"], - green: ["#00ff00", "#111111"], - gold: ["#f9cb16", "#111111"], - bsod: ["#ffffff", "#0000ff"], - starlight: ["#d2b660", "#110717"], - aperture: ["#ffce14", "#1d0b00"], - halloween: ["#ff8000", "#1a120a"], - catppuccin: { - rosewater: ["#f9e2af", "#1e1e2e"], - flamingo: ["#f2cdcd", "#1e1e2e"], - pink: ["#f5c2e7", "#1e1e2e"], - mauve: ["#cba6f7", "#1e1e2e"], - red: ["#f38ba8", "#1e1e2e"], - maroon: ["#eba0ac", "#1e1e2e"], - peach: ["#fab387", "#1e1e2e"], - yellow: ["#f9e2af", "#1e1e2e"], - green: ["#a6e3a1", "#1e1e2e"], - teal: ["#94e2d5", "#1e1e2e"], - sky: ["#89dceb", "#1e1e2e"], - sapphire: ["#74c7ec", "#1e1e2e"], - blue: ["#89b4fa", "#1e1e2e"], - lavendar: ["#b4befe", "#1e1e2e"], - }, - community: { - /* @jorun@meta.jorun.dev */ - jorun: ["#0080ff", "#0d1020"], - /* @meowcatheorange@moth.zone */ - meowca: ["#ff4000", "#130805"], - /* @alcea@pb.todon.de */ - alcea: { - peach: ["#cf4a7299", "#ffffff"], - purple: ["#7f00ff", "#ffffff"], - }, - }, -}; - -function set_palette(palette) { - if (typeof(palette) === "object" && Array.isArray(palette)) { - set_colours(palette[0], palette[1]); - console.log(`Palette changed to [${palette}]`); - return true; - } - const palette_route = palette.split("."); - var palette = PALETTE; - for (var i = 0; i < palette_route.length; i++) { - var palette = palette[palette_route[i]]; - if (!palette) { - console.error(`Palette [${palette_route.join(".")}] does not exist. Enter \`PALETTE\` for a list of palettes.`); - return false; - } - } - set_colours(palette[0], palette[1]); - console.log(`Palette changed to [${palette}].`); - return true; -} - -function set_colours(foreground, background) { - localStorage.setItem("foreground", foreground); - localStorage.setItem("background", background); - document.documentElement.style.setProperty('--colour', foreground); - document.documentElement.style.setProperty('--bgcolour', background); -} - -function clear_colours() { - localStorage.removeItem("foreground"); - localStorage.removeItem("background"); - document.documentElement.style.removeProperty('--colour'); - document.documentElement.style.removeProperty('--bgcolour'); -} - -/** - * toggles LCD theme - */ -function toggle_lcd() { - if (document.body.classList.contains("lcd") || localStorage.getItem("lcd")) { - document.body.classList.remove("lcd"); - localStorage.removeItem("lcd"); - } else { - document.body.classList.add("lcd"); - localStorage.setItem("lcd", true); - } -} +import * as Terminal from "./terminal.js"; +import * as Visual from "./visual.js"; document.addEventListener("DOMContentLoaded", () => { - start(); + Terminal.start(); + Visual.bind(); }); -document.addEventListener("keydown", handle_input); -document.addEventListener("paste", handle_paste); - diff --git a/public/scripts/terminal.js b/public/scripts/terminal.js new file mode 100644 index 0000000..ff4b2fd --- /dev/null +++ b/public/scripts/terminal.js @@ -0,0 +1,266 @@ +var recv_buffer = []; +var send_buffer = []; + +var content; +var mobile_input; + +var client; + +var my_colour = false; +var pre_buffer_chars = 0; +var server_url = ""; + +const DATA_TYPES = { + ping: 0, + text: 1, + colour: 2, + buffer: 3, + backspace: 4, + backword: 5, + arrow: 6, +}; + +export function start() { + console.log("%chello, world!", "color: #b7fd49; font-size: 3rem; font-weight: bold"); + console.log( +`welcome to OpenTerminal! +home to an online terminal and communal text buffer. + +i hope you enjoy your stay here! +to help you feel a little more comfortable, i've prepared some commands for you: + +- set_colours(foreground, background) + changes the foreground and background colours of your terminal! + \`foreground\` and \`background\` must be hex colour codes, such as \`#ff00ff\`. + +- set_palette(palette_name) + changes the foreground and background colours of your terminal to one of our many options of premade themes! including but not limited to the entire collection of catppuccin mocha colours! (i really like their palette ;p) + + try it out! enter \`PALETTE\` into your console and browse the list of themes we have! + +- toggle_lcd() + swaps out the flickering CRT scanline effect for a more modern, LCD screen effect! (bonus: may reduce eye strain)`); + + server_url = new URL(window.location).searchParams.get("server") || window.location.host; + + const foreground = localStorage.getItem("foreground"); + const background = localStorage.getItem("background"); + if (foreground && background) { + set_colours(foreground, background); + } + + if (localStorage.getItem("lcd")) { + document.body.classList.add("lcd"); + } + + content = document.getElementById("content"); + mobile_input = document.getElementById("mobile-input"); + + document.addEventListener("keydown", handle_input); + document.addEventListener("paste", handle_paste); + + content.addEventListener("touchend", () => { + mobile_input.focus(); + }); + + add_system_message("Connecting to the server..."); + + 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(); +} + +function loop() { + mobile_input.value = content.innerText; + + setTimeout(loop, 1000 / 60); +} + +function connect() { + client = new WebSocket("wss://" + server_url); + + client.addEventListener('open', () => { + add_system_message(`\nConnection successful.\n\n`); + add_system_message(`=== BEGIN SESSION ===\n\n`); + new_caret(); + }); + + client.addEventListener('message', event => { handle_message(JSON.parse(event.data)) }); + + client.addEventListener('close', () => { + add_system_message(`\n[CONNECTION LOST, PLEASE REFRESH]\n`); + }); +} + +function add_system_message(text) { + const span = document.createElement("span"); + span.classList.add('sticky'); + span.innerText = text; + content.appendChild(span); + + new_caret(); +} + +function handle_message(data) { + if (!data.type && data.type != 0) return; + + const is_at_bottom = content.scrollHeight - content.offsetHeight - content.scrollTop < 10; + + switch (data.type) { + case DATA_TYPES.ping: + client.send(JSON.stringify({type: DATA_TYPES.ping})); + break; + 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); + break; + case DATA_TYPES.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); + }); + */ + } + + if (pre_buffer_chars == 0) { + pre_buffer_chars = content.innerText.length; + } + + new_caret(); + + if (is_at_bottom) content.scrollTop = content.scrollHeight - content.offsetHeight; +} + +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) { + if (event.key == "'") { + event.preventDefault(); + } + + switch (event.key) { + case "Backspace": + 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"); + if (last_newline > break_point) break_point = last_newline; + const count = content.innerText.length - break_point; + for (var i = 0; i < count; i++) { + 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; + } + + if (event.key.length > 1) { + // server will discard text over 1 character, anyway + return; + } + + if (event.ctrlKey || event.metaKey) { + return; + } + + send_buffer.push({ + type: DATA_TYPES.text, + text: event.key, + }); + + content.scrollTop = content.scrollHeight - content.offsetHeight; +} + +function handle_paste(event) { + event.preventDefault(); + + if (send_buffer.length > 0) { + return; + } + + const paste = (event.clipboardData || window.clipboardData).getData("text"); + send_buffer.push({ + type: DATA_TYPES.text, + text: paste, + }); + content.scrollTop = content.scrollHeight - content.offsetHeight; diff --git a/public/scripts/visual.js b/public/scripts/visual.js new file mode 100644 index 0000000..c483a90 --- /dev/null +++ b/public/scripts/visual.js @@ -0,0 +1,106 @@ +/* + * binds visual functions to the window + */ +export function bind() { + window.PALETTE = PALETTE; + window.set_palette = set_palette; + window.set_colours = set_colours; + window.clear_colours = clear_colours; + window.toggle_lcd = toggle_lcd; +} + +const PALETTE = { + ari: ["#b7fd49", "#111111"], + mono: ["#ffffff", "#111111"], + green: ["#00ff00", "#111111"], + gold: ["#f9cb16", "#111111"], + bsod: ["#ffffff", "#0000ff"], + starlight: ["#d2b660", "#110717"], + aperture: ["#ffce14", "#1d0b00"], + halloween: ["#ff8000", "#1a120a"], + catppuccin: { + rosewater: ["#f9e2af", "#1e1e2e"], + flamingo: ["#f2cdcd", "#1e1e2e"], + pink: ["#f5c2e7", "#1e1e2e"], + mauve: ["#cba6f7", "#1e1e2e"], + red: ["#f38ba8", "#1e1e2e"], + maroon: ["#eba0ac", "#1e1e2e"], + peach: ["#fab387", "#1e1e2e"], + yellow: ["#f9e2af", "#1e1e2e"], + green: ["#a6e3a1", "#1e1e2e"], + teal: ["#94e2d5", "#1e1e2e"], + sky: ["#89dceb", "#1e1e2e"], + sapphire: ["#74c7ec", "#1e1e2e"], + blue: ["#89b4fa", "#1e1e2e"], + lavendar: ["#b4befe", "#1e1e2e"], + }, + community: { + /* @jorun@meta.jorun.dev */ + jorun: ["#0080ff", "#0d1020"], + /* @meowcatheorange@moth.zone */ + meowca: ["#ff4000", "#130805"], + /* @alcea@pb.todon.de */ + alcea: { + peach: ["#cf4a7299", "#ffffff"], + purple: ["#7f00ff", "#ffffff"], + }, + }, +}; + +/* + * sets the colour palette using the name ("example.palette") or reference to a palette (PALETTE.example) + */ +function set_palette(palette) { + if (typeof(palette) === "object" && Array.isArray(palette)) { + set_colours(palette[0], palette[1]); + console.log(`Palette changed to [${palette}]`); + return true; + } + const palette_route = palette.split("."); + var palette = PALETTE; + for (var i = 0; i < palette_route.length; i++) { + var palette = palette[palette_route[i]]; + if (!palette) { + console.error(`Palette [${palette_route.join(".")}] does not exist. Enter \`PALETTE\` for a list of palettes.`); + return false; + } + } + set_colours(palette[0], palette[1]); + console.log(`Palette changed to [${palette}].`); + return true; +} + +/* + * sets the foreground and background colours of the terminal. + * NOTE: this does not override custom colours provided by + * the server. + */ +function set_colours(foreground, background) { + localStorage.setItem("foreground", foreground); + localStorage.setItem("background", background); + document.documentElement.style.setProperty('--colour', foreground); + document.documentElement.style.setProperty('--bgcolour', background); +} + +/* + * returns all custom colour settings to default. + */ +function clear_colours() { + localStorage.removeItem("foreground"); + localStorage.removeItem("background"); + document.documentElement.style.removeProperty('--colour'); + document.documentElement.style.removeProperty('--bgcolour'); +} + +/** + * toggles LCD theme + */ +function toggle_lcd() { + if (document.body.classList.contains("lcd") || localStorage.getItem("lcd")) { + document.body.classList.remove("lcd"); + localStorage.removeItem("lcd"); + } else { + document.body.classList.add("lcd"); + localStorage.setItem("lcd", true); + } +}