Terminal UI
All checks were successful
Deploy Astro / Build and Deploy (push) Successful in 45s

This commit is contained in:
Daniel Kluge 2024-01-19 14:51:27 +01:00
parent bde27d0329
commit 78dc90de5e
3 changed files with 132 additions and 20 deletions

14
package-lock.json generated
View File

@ -10,7 +10,7 @@
"dependencies": { "dependencies": {
"@astrojs/check": "^0.3.1", "@astrojs/check": "^0.3.1",
"@astrojs/mdx": "^1.1.5", "@astrojs/mdx": "^1.1.5",
"@c0ntroller/wasm-terminal": "^0.1.0", "@c0ntroller/wasm-terminal": ">=0.1.0",
"astro": "^3.6.0", "astro": "^3.6.0",
"astro-icon": "^0.8.1", "astro-icon": "^0.8.1",
"sass": "^1.69.5", "sass": "^1.69.5",
@ -526,9 +526,9 @@
} }
}, },
"node_modules/@c0ntroller/wasm-terminal": { "node_modules/@c0ntroller/wasm-terminal": {
"version": "0.1.0", "version": "0.1.1",
"resolved": "https://git.c0ntroller.de/api/packages/c0ntroller/npm/%40c0ntroller%2Fwasm-terminal/-/0.1.0/wasm-terminal-0.1.0.tgz", "resolved": "https://git.c0ntroller.de/api/packages/c0ntroller/npm/%40c0ntroller%2Fwasm-terminal/-/0.1.1/wasm-terminal-0.1.1.tgz",
"integrity": "sha512-mQfm8QcvD6Jq2Eyowiimv0wUWFof+ZQkf7ibXE6Mi5gmkVK7rxOMisn2Tbj8a2RhLnKCL6RutKiKKeH1WGBe4A==", "integrity": "sha512-UQpQWPNCYtx8AYgxWfNYd+KG5qckdAJ4OtwRxIAz7AzJRmFuMZ5Jud0vp+y0tBUla3jqpyybDc4gDiG2sPo2Qw==",
"license": "GPT-3" "license": "GPT-3"
}, },
"node_modules/@emmetio/abbreviation": { "node_modules/@emmetio/abbreviation": {
@ -8712,9 +8712,9 @@
} }
}, },
"@c0ntroller/wasm-terminal": { "@c0ntroller/wasm-terminal": {
"version": "0.1.0", "version": "0.1.1",
"resolved": "https://git.c0ntroller.de/api/packages/c0ntroller/npm/%40c0ntroller%2Fwasm-terminal/-/0.1.0/wasm-terminal-0.1.0.tgz", "resolved": "https://git.c0ntroller.de/api/packages/c0ntroller/npm/%40c0ntroller%2Fwasm-terminal/-/0.1.1/wasm-terminal-0.1.1.tgz",
"integrity": "sha512-mQfm8QcvD6Jq2Eyowiimv0wUWFof+ZQkf7ibXE6Mi5gmkVK7rxOMisn2Tbj8a2RhLnKCL6RutKiKKeH1WGBe4A==" "integrity": "sha512-UQpQWPNCYtx8AYgxWfNYd+KG5qckdAJ4OtwRxIAz7AzJRmFuMZ5Jud0vp+y0tBUla3jqpyybDc4gDiG2sPo2Qw=="
}, },
"@emmetio/abbreviation": { "@emmetio/abbreviation": {
"version": "2.3.3", "version": "2.3.3",

View File

@ -12,7 +12,7 @@
"dependencies": { "dependencies": {
"@astrojs/check": "^0.3.1", "@astrojs/check": "^0.3.1",
"@astrojs/mdx": "^1.1.5", "@astrojs/mdx": "^1.1.5",
"@c0ntroller/wasm-terminal": "^0.1.0", "@c0ntroller/wasm-terminal": ">=0.1.0",
"astro": "^3.6.0", "astro": "^3.6.0",
"astro-icon": "^0.8.1", "astro-icon": "^0.8.1",
"sass": "^1.69.5", "sass": "^1.69.5",

View File

@ -1,33 +1,89 @@
--- ---
import Layout from "../layouts/Layout.astro"; import Layout from "../layouts/Layout.astro";
import wasmData from "@c0ntroller/wasm-terminal/package.json";
const date = new Date();
const screenVersion = `${date.getFullYear()}${date.getMonth()+1}${date.getDate()}${date.getHours()}${date.getMinutes()}`;
const wasmVersion = wasmData.version;
--- ---
<Layout title="Terminal"> <Layout title="Terminal">
<div class="screen"> <div class="screen" data-terminal-screen>
<pre data-terminal-output></pre> <div data-terminal-output>
<noscript><pre>You need to enable JavaScript to use this terminal.</pre></noscript>
<pre>cer0 0S V{wasmVersion} - UI V{screenVersion}</pre>
<pre class="logo">@@@@@@@@@@@
@@@@@@@@@@@@
@@@@@@@@@@@@
@@@@@@@@@@@@@
@@@@ @@@@@@@@@@@@
@@@@@ @@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@ @@@@@@@@@
@@@@@@@@@@@@@@@W#MW@@@@@@@@@@@@@@@ @@@@@@@@@
@@@@@########*****pkaMB@@@@@@@@mb@@@@@ @@@@@@@
@@@@@*************pwdhoh@@@@@$OOOOOw*##@@@@ @ @@@@
@@@@@************dOOOOOOOOOO0OOOOOO*******#@@ @ @@@
@@@@************pOOOOOOOOOOOOOOZ#**********#@@ @
@@@@************oOOOOOOOOOOOOa***************#@
@@@@@***********odwwpdda**********************
@@@@@**********************************
@@@@@@@@@$#*###MMB@@@@@@@@@@@@</pre>
</div> </div>
<input type="text" data-terminal-input /> <div class="input"><pre class="user">user@cer0</pre><pre>:</pre><pre class="pwd">/</pre><pre class="dollar">$&nbsp;</pre><input type="text" data-terminal-input autofocus /></div>
</div>
<template data-terminal-user-cmd>
<pre><pre class="user">user@cer0</pre><pre>:</pre><pre class="pwd"></pre><pre class="dollar">$&nbsp;</pre><pre class="user-cmd"></pre></pre>
</template>
</Layout> </Layout>
<script> <script>
(async () => { (async () => {
const wasm = import("@c0ntroller/wasm-terminal"); const wasm = import("@c0ntroller/wasm-terminal");
const outputs = document.querySelectorAll("[data-terminal-output]") as NodeListOf<HTMLPreElement>; const outputs = document.querySelectorAll("[data-terminal-output]") as NodeListOf<HTMLPreElement>;
const inputs = document.querySelectorAll("[data-terminal-input]") as NodeListOf<HTMLInputElement>; const inputs = document.querySelectorAll("[data-terminal-input]") as NodeListOf<HTMLInputElement>;
const screens = document.querySelectorAll("[data-terminal-screen]") as NodeListOf<HTMLDivElement>;
const template = document.querySelector("[data-terminal-user-cmd]") as HTMLTemplateElement;
screens.forEach((s) => s.addEventListener("click", () => inputs[0]?.focus()))
const { default: init, Console } = await wasm; const { default: init, Console } = await wasm;
await init(); await init();
const c = Console.new(); const c = Console.new();
function updatePwd() {
document.querySelector(".input .pwd")!.textContent = c.get_pwd();
}
updatePwd();
inputs.forEach((input) => { inputs.forEach((input) => {
input.addEventListener("keydown", (e) => { input.addEventListener("keydown", (e) => {
if (e.key === "Enter") { if (e.key === "Enter") {
const prevPwd = c.get_pwd();
const cmd = input.value; const cmd = input.value;
const resp = c.execute(cmd);
const inputLine = template.content.cloneNode(true) as HTMLPreElement;
inputLine.querySelector(".user-cmd")!.textContent = cmd;
inputLine.querySelector(".pwd")!.textContent = prevPwd;
outputs.forEach((output) => { outputs.forEach((output) => {
output.innerHTML += `$ ${cmd}${resp ? `\n${resp}` : ""}\n`; output.prepend(inputLine);
}); });
const resp = c.execute(cmd);
if (resp) {
const outputLine = document.createElement("pre");
outputLine.textContent = resp
outputs.forEach((output) => {
output.prepend(outputLine);
});
}
input.value = ""; input.value = "";
updatePwd();
} }
}); });
}); });
@ -35,22 +91,78 @@ import Layout from "../layouts/Layout.astro";
</script> </script>
<style lang="scss"> <style lang="scss">
@font-face {
font-family: "Cascadia Code";
src: url("/fonts/CascadiaCode.woff2") format("woff2");
}
.screen { .screen {
width: 100%; width: 100%;
height: 60em; height: 30em;
background: #000; background: #000;
border-radius: 0.2em; border-radius: 0.2em;
box-shadow: 0 0 0.5em #000; box-shadow: 0 0 0.5em #000;
color: #fff; color: #fff;
font-family: monospace;
padding: 10px; padding: 10px;
display: flex;
flex-direction: column;
overflow-y: scroll;
pre { pre > pre {
display: inline-block;
}
.logo {
overflow-wrap: normal !important;
white-space: pre !important;
overflow-x: hidden;
font-size: min(2vw, 1em);
}
[data-terminal-output] {
display: flex;
flex-direction: column-reverse;
}
}
:global(pre) {
margin: 0; margin: 0;
padding: 0; padding: 0;
white-space: pre-wrap; white-space: pre-wrap;
word-wrap: normal; word-wrap: normal;
overflow-wrap: break-word; overflow-wrap: break-word;
font-family: "Cascadia Code", monospace;
&.user {
color: #0f0;
}
&.pwd {
color: #00f;
}
}
.input {
display: flex;
flex-direction: row;
input {
font-family: "Cascadia Code", monospace;
display: inline-block;
flex-grow: 2;
font-size: 1rem;
padding: 0;
margin: 0;
border: none;
background: transparent;
appearance: none !important;
outline: none;
caret-shape: block;
&::-moz-focus-outer, &::-moz-focus-inner, &:focus, &:focus *, &:-moz-focusring, &:-moz-focusring * {
border: none !important;
outline: none !important;
box-shadow: none !important;
}
} }
} }