Upgrade template to v3

This commit is contained in:
Florine W. Dekker 2022-11-26 14:43:33 +01:00
parent 2abf7538c0
commit 9aeb3ec2f9
Signed by: FWDekker
GPG Key ID: D3DCFAA8A4560BE0
12 changed files with 119 additions and 113 deletions

BIN
package-lock.json generated

Binary file not shown.

View File

@ -1,6 +1,6 @@
{ {
"name": "fwdekker.com", "name": "fwdekker.com",
"version": "0.40.13", "version": "0.40.14",
"description": "The source code of [my personal website](https://fwdekker.com/).", "description": "The source code of [my personal website](https://fwdekker.com/).",
"author": "Florine W. Dekker", "author": "Florine W. Dekker",
"browser": "dist/bundle.js", "browser": "dist/bundle.js",
@ -24,27 +24,27 @@
}, },
"devDependencies": { "devDependencies": {
"@istanbuljs/nyc-config-typescript": "^1.0.2", "@istanbuljs/nyc-config-typescript": "^1.0.2",
"@types/chai": "^4.3.0", "@types/chai": "^4.3.4",
"@types/js-cookie": "^3.0.1", "@types/js-cookie": "^3.0.2",
"@types/mocha": "^9.1.0", "@types/mocha": "^10.0.0",
"@types/semver": "^6.2.1", "@types/semver": "^7.3.13",
"chai": "^4.3.6", "chai": "^4.3.7",
"grunt": "^1.4.1", "grunt": "^1.5.3",
"grunt-cli": "^1.4.3", "grunt-cli": "^1.4.3",
"grunt-contrib-clean": "^2.0.0", "grunt-contrib-clean": "^2.0.1",
"grunt-contrib-copy": "^1.0.0", "grunt-contrib-copy": "^1.0.0",
"grunt-contrib-watch": "^1.1.0", "grunt-contrib-watch": "^1.1.0",
"grunt-focus": "^1.0.0", "grunt-focus": "^1.0.0",
"grunt-text-replace": "^0.4.0", "grunt-text-replace": "^0.4.0",
"grunt-webpack": "^5.0.0", "grunt-webpack": "^5.0.0",
"jsdom": "^19.0.0", "jsdom": "^20.0.3",
"jsdom-global": "^3.0.2", "jsdom-global": "^3.0.2",
"mocha": "^9.2.1", "mocha": "^10.1.0",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"ts-loader": "^9.2.6", "ts-loader": "^9.4.1",
"ts-node": "^10.5.0", "ts-node": "^10.9.1",
"typescript": "^4.5.5", "typescript": "^4.9.3",
"webpack": "^5.69.1", "webpack": "^5.75.0",
"webpack-cli": "^4.9.2" "webpack-cli": "^5.0.0"
} }
} }

View File

@ -1,64 +1,58 @@
/* Main elements */
* {
box-sizing: border-box;
}
html,
body,
body > main {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
background-color: black;
}
noscript ul { noscript ul {
text-align: center; text-align: center;
} }
.fwd-nav {
#terminal a { position: sticky;
cursor: pointer; top: 0;
text-decoration: none;
} }
#terminal a, #terminal a:link, #terminal a:visited { .error-message {
color: #00FF00; color: #FF3333;
}
#terminal a:hover {
color: #00BF00;
text-decoration: underline;
}
#terminal a:link.dirLink, #terminal a:visited.dirLink {
color: #00FF00;
}
#terminal a:link.fileLink, #terminal a:visited.fileLink {
color: #FFFF00;
}
.prefixPath, .prefixPath > a:link, .prefixPath > a:visited {
color: #008000;
} }
/* Mobile support */
@media (max-width: 600px) { @media (max-width: 600px) {
.wideScreenOnly { .wide-screen-only {
display: none; display: none;
} }
.smallScreenOnly { .small-screen-only {
display: inline; display: inline;
} }
} }
@media (min-width: 600px) { @media (min-width: 600px) {
.wideScreenOnly { .wide-screen-only {
display: inline; display: inline;
} }
.smallScreenOnly { .small-screen-only {
display: none; display: none;
} }
} }
* { /* Terminal */
box-sizing: border-box;
}
body {
background-color: black;
}
#terminal { #terminal {
min-height: 100%; min-height: 100%;
padding: 25px; padding: 25px;
@ -77,20 +71,20 @@ body {
margin: 0; margin: 0;
} }
#terminalInput { #terminal-input {
display: inline-block; display: inline-block;
} }
#terminalInputField { #terminal-input-field {
outline: none; outline: none;
border: none; border: none;
} }
#terminalInputField br { #terminal-input-field br {
display: none; display: none;
} }
.terminalInputFieldHidden { .terminal-input-field-hidden {
max-width: 1px !important; max-width: 1px !important;
overflow: hidden !important; overflow: hidden !important;
overflow-wrap: normal !important; overflow-wrap: normal !important;
@ -98,17 +92,40 @@ body {
vertical-align: top !important; vertical-align: top !important;
} }
#terminalSuggestions { #terminal-suggestions {
display: block; display: block;
} }
.errorMessage { /* Links */
color: #FF3333; #terminal a {
cursor: pointer;
text-decoration: none;
} }
#terminal a,
#nav { #terminal a:link,
position: sticky; #terminal a:visited {
top: 0; color: #00FF00;
}
#terminal a:hover {
color: #00BF00;
text-decoration: underline;
}
#terminal a:link.dir-link,
#terminal a:visited.dir-link {
color: #00FF00;
}
#terminal a:link.file-link,
#terminal a:visited.file-link {
color: #FFFF00;
}
.prefix-path,
.prefix-path > a:link,
.prefix-path > a:visited {
color: #008000;
} }

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en" data-theme="dark">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
@ -12,6 +12,9 @@
<meta property="og:description" content="Florine W. Dekker's personal website"> <meta property="og:description" content="Florine W. Dekker's personal website">
<meta property="og:locale" content="en_GB" /> <meta property="og:locale" content="en_GB" />
<meta name="fwd:nav:target" content="#nav" />
<meta name="fwd:nav:highlight-path" content="//" />
<title>FWDekker</title> <title>FWDekker</title>
<!--suppress HtmlUnknownTarget --> <!--suppress HtmlUnknownTarget -->
@ -20,17 +23,16 @@
<link rel="apple-touch-icon" href="icon_ios.png?v=%%VERSION_NUMBER%%" /> <link rel="apple-touch-icon" href="icon_ios.png?v=%%VERSION_NUMBER%%" />
<link rel="manifest" href="manifest.json?v=%%VERSION_NUMBER%%"> <link rel="manifest" href="manifest.json?v=%%VERSION_NUMBER%%">
<link rel="stylesheet" href="https://static.fwdekker.com/lib/template/2.x.x/template.css" />
<link rel="stylesheet" href="https://static.fwdekker.com/fonts/roboto-mono/roboto-mono.css" /> <link rel="stylesheet" href="https://static.fwdekker.com/fonts/roboto-mono/roboto-mono.css" />
<link rel="stylesheet" href="https://static.fwdekker.com/lib/template/3.x.x/template.css?v=%%VERSION_NUMBER%%" />
<!--suppress HtmlUnknownTarget --> <!--suppress HtmlUnknownTarget -->
<link rel="stylesheet" href="main.css?v=%%VERSION_NUMBER%%" /> <link rel="stylesheet" href="main.css?v=%%VERSION_NUMBER%%" />
<script async src="https://stats.fwdekker.com/count.js" <script async src="https://stats.fwdekker.com/count.js"
data-goatcounter="https://stats.fwdekker.com/count"></script> data-goatcounter="https://stats.fwdekker.com/count"></script>
</head> </head>
<body> <body>
<nav id="nav"></nav>
<main> <main>
<div id="nav"></div>
<!-- Comment out newlines and indents because of `white-space: pre-wrap` in CSS. --> <!-- Comment out newlines and indents because of `white-space: pre-wrap` in CSS. -->
<div id="terminal"><!-- <div id="terminal"><!--
--><noscript><!-- --><noscript><!--
@ -51,24 +53,19 @@
--><li><a href="https://fwdekker.com/api/nav/">JSON site map</a></li><!-- --><li><a href="https://fwdekker.com/api/nav/">JSON site map</a></li><!--
--></ul><!-- --></ul><!--
--></noscript><!-- --></noscript><!--
--><div id="ie-warning" class="hidden"><!-- --><span id="terminal-output"></span><!--
-->This website does not function with Internet Explorer. <!-- --><span id="terminal-input"><!--
-->Please install a newer browser such as <!-- --><span id="terminal-input-prefix"></span><!--
--><a href="https://www.microsoft.com/en-us/windows/microsoft-edge">Microsoft Edge</a>.<!-- --><span id="terminal-input-field" contenteditable="true" autocapitalize="none"
--></div><!--
--><span id="terminalOutput"></span><!--
--><span id="terminalInput"><!--
--><span id="terminalInputPrefix"></span><!--
--><span id="terminalInputField" contenteditable="true" autocapitalize="none"
spellcheck="false"></span><!-- spellcheck="false"></span><!--
--></span><!-- --></span><!--
--><span id="terminalSuggestions"></span><!-- --><span id="terminal-suggestions"></span><!--
--></div> --></div>
</main> </main>
<!--suppress HtmlUnknownTarget --> <!--suppress HtmlUnknownTarget -->
<script src="plain.js?v=%%VERSION_NUMBER%%"></script> <script src="plain.js?v=%%VERSION_NUMBER%%"></script>
<script src="https://static.fwdekker.com/lib/template/2.x.x/template.js"></script> <script src="https://static.fwdekker.com/lib/template/3.x.x/template.js?v=%%VERSION_NUMBER%%"></script>
<!--suppress HtmlUnknownTarget --> <!--suppress HtmlUnknownTarget -->
<script type="module" src="bundle.js?v=%%VERSION_NUMBER%%"></script> <script type="module" src="bundle.js?v=%%VERSION_NUMBER%%"></script>
</body> </body>

View File

@ -573,7 +573,7 @@ export class Directory extends Node {
* @param path the path to this node * @param path the path to this node
*/ */
nameString(name: string, path: Path): string { nameString(name: string, path: Path): string {
return `<a class="dirLink" onclick="execute('${path.toString(true)}; and ls -l')">${name}</a>`; return `<a class="dir-link" onclick="execute('${path.toString(true)}; and ls -l')">${name}</a>`;
} }
visit(path: string, visit(path: string,
@ -673,15 +673,15 @@ export class File extends Node {
switch (this.mime ?? getFileExtension(name)) { switch (this.mime ?? getFileExtension(name)) {
case "jsh": { case "jsh": {
const script = `execute('${path.toString(true)}'); return false`; const script = `execute('${path.toString(true)}'); return false`;
return `<a class="fileLink" onclick="${script}">${name}</a>`; return `<a class="file-link" onclick="${script}">${name}</a>`;
} }
case "lnk": { case "lnk": {
const script = `execute('open ${path.toString(true)}'); return false`; const script = `execute('open ${path.toString(true)}'); return false`;
return `<a href="${this.contents}" class="fileLink" onclick="${script}">${name}</a>`; return `<a href="${this.contents}" class="file-link" onclick="${script}">${name}</a>`;
} }
case "txt": { case "txt": {
const script = `execute('cat ${path.toString(true)}')`; const script = `execute('cat ${path.toString(true)}')`;
return `<a class="fileLink" onclick="${script}">${name}</a>`; return `<a class="file-link" onclick="${script}">${name}</a>`;
} }
default: default:
return name; return name;

View File

@ -1,6 +1,6 @@
const {$, doAfterLoad} = (window as any).fwdekker;
import * as semver from "semver"; import * as semver from "semver";
// @ts-ignore
const {$, doAfterLoad, nav} = window.fwdekker;
import {Persistence} from "./Persistence"; import {Persistence} from "./Persistence";
import {ExpectedGoodbyeError} from "./Shared"; import {ExpectedGoodbyeError} from "./Shared";
import {Terminal} from "./Terminal"; import {Terminal} from "./Terminal";
@ -37,8 +37,8 @@ doAfterLoad(() => {
} }
if (Persistence.getWasUpdated()) { if (Persistence.getWasUpdated()) {
$("#terminalOutput").innerHTML = "" + $("#terminal-output").innerHTML = "" +
"<span class=\"errorMessage\">The terminal application has been updated. To prevent unexpected errors, " + "<span class=\"error-message\">The terminal application has been updated. To prevent unexpected errors, " +
"all previous user changes have been reset.</span>\n\n"; "all previous user changes have been reset.</span>\n\n";
Persistence.setWasUpdated(false); Persistence.setWasUpdated(false);
} }
@ -52,7 +52,7 @@ doAfterLoad(() => {
doAfterLoad(() => { doAfterLoad(() => {
if (!Persistence.getPoweroff()) return; if (!Persistence.getPoweroff()) return;
$("#terminalOutput").innerText = "Could not connect to fwdekker.com. Retrying in 10 seconds."; $("#terminal-output").innerText = "Could not connect to fwdekker.com. Retrying in 10 seconds.";
setTimeout(() => location.reload(), 10000); setTimeout(() => location.reload(), 10000);
throw new ExpectedGoodbyeError("Goodbye"); throw new ExpectedGoodbyeError("Goodbye");
}); });
@ -61,14 +61,12 @@ doAfterLoad(() => {
* Initializes the application. * Initializes the application.
*/ */
doAfterLoad(async () => { doAfterLoad(async () => {
$("#nav").appendChild(nav("/"));
window.terminal = new Terminal( window.terminal = new Terminal(
$("#terminal"), $("#terminal"),
$("#terminalInputField"), $("#terminal-input-field"),
$("#terminalOutput"), $("#terminal-output"),
$("#terminalInputPrefix"), $("#terminal-input-prefix"),
$("#terminalSuggestions") $("#terminal-suggestions")
); );
window.execute = (command: string) => window.terminal.processInput(command); window.execute = (command: string) => window.terminal.processInput(command);

View File

@ -1,4 +1,5 @@
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import {Environment} from "./Environment"; import {Environment} from "./Environment";
import {Directory, FileSystem, Node} from "./FileSystem"; import {Directory, FileSystem, Node} from "./FileSystem";
import {InputHistory} from "./InputHistory"; import {InputHistory} from "./InputHistory";
@ -133,7 +134,7 @@ export class Persistence {
* @param value whether the server is "turned off" * @param value whether the server is "turned off"
*/ */
static setPoweroff(value: boolean): void { static setPoweroff(value: boolean): void {
Cookies.set("poweroff", "" + value, { Cookies?.set("poweroff", "" + value, {
expires: new Date(new Date().setSeconds(new Date().getSeconds() + 30)), expires: new Date(new Date().setSeconds(new Date().getSeconds() + 30)),
path: "/", path: "/",
secure: true, secure: true,

View File

@ -16,7 +16,7 @@ const asciiHeader = `&nbsp;________ _______ _ _
* large enough. * large enough.
*/ */
export const asciiHeaderHtml = export const asciiHeaderHtml =
`<span class="wideScreenOnly">${asciiHeader}</span><span class="smallScreenOnly"><b><u>FWDekker</u></b></span>`; `<span class="wide-screen-only">${asciiHeader}</span><span class="small-screen-only"><b><u>FWDekker</u></b></span>`;
/** /**
* A function that does nothing. * A function that does nothing.

View File

@ -1,12 +1,11 @@
import {Commands, ExitCode} from "./Commands"; import {Commands, ExitCode} from "./Commands";
import {Environment} from "./Environment"; import {Environment} from "./Environment";
import {Directory, FileSystem, Path} from "./FileSystem"; import {Directory, FileSystem, Path} from "./FileSystem";
import {InputArgs} from "./InputArgs";
import {InputHistory} from "./InputHistory"; import {InputHistory} from "./InputHistory";
import {Globber, InputParser} from "./InputParser"; import {Globber, InputParser} from "./InputParser";
import {Persistence} from "./Persistence"; import {Persistence} from "./Persistence";
import {asciiHeaderHtml, ExpectedGoodbyeError, IllegalStateError, isStandalone} from "./Shared"; import {asciiHeaderHtml, ExpectedGoodbyeError, isStandalone} from "./Shared";
import {OutputStream, StreamSet} from "./Stream"; import {StreamSet} from "./Stream";
import {EscapeCharacters} from "./Terminal"; import {EscapeCharacters} from "./Terminal";
import {UserList} from "./UserList"; import {UserList} from "./UserList";
@ -69,9 +68,9 @@ export class Shell {
const target = isStandalone() ? `target="_blank"` : ""; const target = isStandalone() ? `target="_blank"` : "";
return `${asciiHeaderHtml} return `${asciiHeaderHtml}
PhD student Computer Science <span class="smallScreenOnly"> PhD student Computer Science <span class="small-screen-only">
</span>@ <a href="https://www.tudelft.nl/en/" ${target}>TU Delft</a>, the Netherlands </span>@ <a href="https://www.tudelft.nl/en/" ${target}>TU Delft</a>, the Netherlands
<span class="wideScreenOnly">${(new Date()).toISOString()} <span class="wide-screen-only">${(new Date()).toISOString()}
</span> </span>
Type "<a onclick="execute('help');">help</a>" for help. Type "<a onclick="execute('help');">help</a>" for help.
@ -111,9 +110,9 @@ export class Shell {
.join("/"); .join("/");
const status = this.environment.get("status"); const status = this.environment.get("status");
const statusString = status === "0" ? "" : ` <span class="errorMessage">[${status}]</span>`; const statusString = status === "0" ? "" : ` <span class="error-message">[${status}]</span>`;
return `${userName}@fwdekker.com <span class="prefixPath">${rootText}${partText}</span>${statusString}&gt; `; return `${userName}@fwdekker.com <span class="prefix-path">${rootText}${partText}</span>${statusString}&gt; `;
} }

View File

@ -72,7 +72,7 @@ export class Terminal {
} }
write(string: string) { write(string: string) {
this.wrappedBuffer.write(`<span class="errorMessage">${string}</span>`); this.wrappedBuffer.write(`<span class="error-message">${string}</span>`);
} }
}(this.standardOutput); }(this.standardOutput);
@ -170,7 +170,7 @@ export class Terminal {
* Returns `true` if and only if the input field does not display the user's input. * Returns `true` if and only if the input field does not display the user's input.
*/ */
private get isInputHidden(): boolean { private get isInputHidden(): boolean {
return this.input.classList.contains("terminalInputFieldHidden"); return this.input.classList.contains("terminal-input-field-hidden");
} }
/** /**
@ -180,9 +180,9 @@ export class Terminal {
*/ */
private set isInputHidden(isInputHidden: boolean) { private set isInputHidden(isInputHidden: boolean) {
if (isInputHidden) if (isInputHidden)
this.input.classList.add("terminalInputFieldHidden"); this.input.classList.add("terminal-input-field-hidden");
else else
this.input.classList.remove("terminalInputFieldHidden"); this.input.classList.remove("terminal-input-field-hidden");
} }

View File

@ -1,9 +1,3 @@
if (/MSIE|Trident/.test(window.navigator.userAgent)) {
window.onload = function() {
document.getElementById("ie-warning").className = "";
};
}
if ("serviceWorker" in navigator) { if ("serviceWorker" in navigator) {
window.addEventListener("load", function() { window.addEventListener("load", function() {
return navigator.serviceWorker.register("sw.js?v=%%VERSION_NUMBER%%"); return navigator.serviceWorker.register("sw.js?v=%%VERSION_NUMBER%%");

View File

@ -52,17 +52,17 @@ describe("file", () => {
it("uses the file extension to determine the value", () => { it("uses the file extension to determine the value", () => {
expect(new File("contents").nameString("file.txt", new Path("/file"))) expect(new File("contents").nameString("file.txt", new Path("/file")))
.to.equal(`<a class="fileLink" onclick="execute('cat /file')">file.txt</a>`); .to.equal(`<a class="file-link" onclick="execute('cat /file')">file.txt</a>`);
}); });
it("uses the mime type if no type is known", () => { it("uses the mime type if no type is known", () => {
expect(new File("contents", "txt").nameString("file", new Path("/file"))) expect(new File("contents", "txt").nameString("file", new Path("/file")))
.to.equal(`<a class="fileLink" onclick="execute('cat /file')">file</a>`); .to.equal(`<a class="file-link" onclick="execute('cat /file')">file</a>`);
}); });
it("overrides the file extension with the mime type", () => { it("overrides the file extension with the mime type", () => {
expect(new File("link", "lnk").nameString("file.txt", new Path("/file"))) expect(new File("link", "lnk").nameString("file.txt", new Path("/file")))
.to.equal(`<a href="link" class="fileLink" onclick="execute('open /file'); return false">file.txt</a>`); .to.equal(`<a href="link" class="file-link" onclick="execute('open /file'); return false">file.txt</a>`);
}); });
}); });
}); });