mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-09 14:20:49 -08:00
fix(#700): fix terminal emulator on mobile
This commit is contained in:
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 60rem;
|
max-width: 640px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,10 +32,7 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The "terminal" has one "prompt" element.
|
/* The "terminal" has one "prompt" input-element. */
|
||||||
* This prompt is not any kind of input, but just a simple <span>
|
|
||||||
* with an id "prompt" and a
|
|
||||||
*/
|
|
||||||
@keyframes prompt-blink {
|
@keyframes prompt-blink {
|
||||||
100% {
|
100% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@@ -57,6 +54,15 @@
|
|||||||
width: 0.75rem;
|
width: 0.75rem;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
.terminal input#prompt {
|
||||||
|
text-transform: uppercase;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
caret-color: var(--text);
|
||||||
|
color: var(--text);
|
||||||
|
font: var(--terminal-font);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Terminal scrollbar */
|
/* Terminal scrollbar */
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class HtmlTerminal {
|
|||||||
* @private
|
* @private
|
||||||
* @type {HTMLElement}
|
* @type {HTMLElement}
|
||||||
*/
|
*/
|
||||||
#$prompt = undefined;
|
#$prompt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
@@ -45,13 +45,18 @@ class HtmlTerminal {
|
|||||||
this.$output.classList.add('terminal');
|
this.$output.classList.add('terminal');
|
||||||
|
|
||||||
// Create a prompt element.
|
// Create a prompt element.
|
||||||
// This element gets added if input is needed
|
// This element gets added if input is needed.
|
||||||
this.#$prompt = document.createElement("span");
|
this.#$prompt = document.createElement("input");
|
||||||
this.#$prompt.setAttribute("id", "prompt");
|
this.#$prompt.setAttribute("id", "prompt");
|
||||||
this.#$prompt.innerText = "";
|
this.#$prompt.setAttribute("type", "text");
|
||||||
|
this.#$prompt.setAttribute("length", "50");
|
||||||
|
this.#$prompt.addEventListener("keydown", this.#handleKey.bind(this));
|
||||||
|
|
||||||
//TODO: this handler shouls be only on the propt element and only active if cursor is visible
|
// Force focus on the promt on each click.
|
||||||
document.addEventListener("keyup", this.#handleKey.bind(this));
|
// This is needed for mobile support.
|
||||||
|
document.body.addEventListener('click', () => {
|
||||||
|
this.#$prompt.focus();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,37 +82,16 @@ class HtmlTerminal {
|
|||||||
* @param {*} e
|
* @param {*} e
|
||||||
*/
|
*/
|
||||||
#handleKey(e) {
|
#handleKey(e) {
|
||||||
// if no input-callback is defined
|
// if no input-callback is defined just return
|
||||||
if (!this.#inputCallback) {
|
if (!this.#inputCallback) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.keyCode === 13 /* ENTER */) {
|
if (e.keyCode == 13) {
|
||||||
// create a new line with the text input and remove the prompt
|
const text = this.#$prompt.value;
|
||||||
const text = this.#$prompt.innerText;
|
this.#$prompt.value = '';
|
||||||
this.write(text + "\n");
|
|
||||||
this.#$prompt.innerText = "";
|
|
||||||
this.#$prompt.remove();
|
this.#$prompt.remove();
|
||||||
|
this.#inputCallback(text + '\n');
|
||||||
// return the inputed text
|
|
||||||
this.#inputCallback(text);
|
|
||||||
|
|
||||||
// remove the callback and the key handler
|
|
||||||
this.#inputCallback = undefined;
|
|
||||||
} else if (e.keyCode === 8 /* BACKSPACE */) {
|
|
||||||
this.#$prompt.innerText = this.#$prompt.innerText.slice(0, -1);
|
|
||||||
} else if (
|
|
||||||
e.keyCode == 16 // "Shift"
|
|
||||||
|| e.keyCode == 17 // "Control"
|
|
||||||
|| e.keyCode == 20 // "CapsLock"
|
|
||||||
|| !e.key.match(/^[a-z0-9!"§#$%&'()*+,.\/:;<=>?@\[\] ^_`{|}~-]$/i)
|
|
||||||
) {
|
|
||||||
// ignore non-visible characters
|
|
||||||
return e;
|
|
||||||
} else {
|
|
||||||
this.#$prompt.innerHtml = '';
|
|
||||||
const key = e.shiftKey ? e.key.toUpperCase() : e.key;
|
|
||||||
this.#$prompt.innerText = this.#$prompt.innerText + key;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +106,7 @@ class HtmlTerminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO:
|
* Create a new div and add html content.
|
||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
* @param {*} htmlContent
|
* @param {*} htmlContent
|
||||||
@@ -189,7 +173,8 @@ class HtmlTerminal {
|
|||||||
*/
|
*/
|
||||||
input(callback) {
|
input(callback) {
|
||||||
// show prompt with a blinking prompt
|
// show prompt with a blinking prompt
|
||||||
this.$output.appendChild(this.#$prompt);
|
|
||||||
this.#inputCallback = callback;
|
this.#inputCallback = callback;
|
||||||
|
this.$output.appendChild(this.#$prompt);
|
||||||
|
this.#$prompt.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Minimal node.js terminal</title>
|
<title>Minimal node.js terminal</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=.75">
|
<meta name="viewport" content="width=640, initial-scale=1">
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="../../../00_Utilities/javascript/style_terminal.css"
|
href="../../../00_Utilities/javascript/style_terminal.css"
|
||||||
|
|||||||
@@ -57,9 +57,13 @@ export async function input(message = '') {
|
|||||||
* First we need to convert it into a string. */
|
* First we need to convert it into a string. */
|
||||||
const data = input.toString();
|
const data = input.toString();
|
||||||
|
|
||||||
|
/* add input to terminal
|
||||||
|
* The data should end with a newline! */
|
||||||
|
process.stdout.write(data);
|
||||||
|
|
||||||
/* The result fo onData is a string ending with an `\n`.
|
/* The result fo onData is a string ending with an `\n`.
|
||||||
* We just need the actual content so let's remove the newline at the end: */
|
* We just need the actual content so let's remove the newline at the end: */
|
||||||
const content = data[data.length] === '\n' ? data.slice(0, -1) : data;
|
const content = data.endsWith('\n') ? data.slice(0, -1) : data;
|
||||||
|
|
||||||
resolve(content);
|
resolve(content);
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user