diff --git a/www/css/ppty.css b/www/css/ppty.css
new file mode 100644
index 0000000..73192fb
--- /dev/null
+++ b/www/css/ppty.css
@@ -0,0 +1,50 @@
+/* =========================================================== *
+ * Pseudo-Pseudo-Terminal Effect *
+/* =========================================================== */
+
+.ppty {
+ background-color: var(--theme-bg);
+ border: 0.5ch solid var(--theme-tty-border);
+
+ margin: auto auto;
+ padding: 20px;
+
+ position: relative;
+ height: auto;
+
+ /* display: block; */
+ /* flex-direction: column; */
+ /* justify-content: start; */
+ /* align-content: center; */
+ /* align-items: start; */
+
+ overflow: hidden;
+}
+
+.ppty-block {
+ color: var(--theme-tty-output);
+
+ visibility: hidden;
+ white-space: nowrap; /* only break on
*/
+}
+
+.ppty-prompt {
+ color: var(--theme-tty-prompt);
+
+ display: inline-block;
+ vertical-align: top;
+}
+
+.ppty-command {
+ color: var(--theme-tty-command);
+
+ width: 0px;
+ border-right: 1ch solid var(--theme-tty-output);
+
+ display: inline-block;
+ vertical-align: top;
+ overflow: hidden;
+}
+
+.ppty-output {
+}
diff --git a/www/css/shader-style.css b/www/css/shader-style.css
deleted file mode 100644
index 9b39002..0000000
--- a/www/css/shader-style.css
+++ /dev/null
@@ -1,11 +0,0 @@
-.gl-canvas-bg {
- display:block;
- width: 100vw;
- height: 100vh;
-
- position: fixed;
- left: 0;
- top: 0;
-
- z-index: -1;
-}
diff --git a/www/css/style.css b/www/css/style.css
index 9a14616..f4d9a73 100644
--- a/www/css/style.css
+++ b/www/css/style.css
@@ -1,3 +1,27 @@
body {
background-color: var(--theme-bg);
}
+
+#bg-canvas {
+ display:block;
+ position: fixed;
+
+ inset: 0;
+ width: 100vw;
+ height: 100vh;
+ z-index: -1;
+}
+
+.centered {
+ position: absolute;
+ inset: 0 0 0 0;
+ margin: auto;
+
+ display: flex;
+}
+
+.heading {
+ font-family: monospace;
+ font-size: 2em;
+ font-weight: bold;
+}
diff --git a/www/css/typing.css b/www/css/typing.css
index 0dd1d88..9426269 100644
--- a/www/css/typing.css
+++ b/www/css/typing.css
@@ -10,7 +10,6 @@
font-family: monospace;
font-size: 2em;
font-weight: bold;
- color: #ffc0cb; /* #ac4aed */
}
/* =========================================================== *
@@ -19,100 +18,97 @@
#typing-wrapper {
margin: auto auto;
- /* width: 71ch; /* prompt + command + cursor length */
- /* height: 21ch; */
text-align: start;
- border: 0.5ch solid #ffc0cb; /* #ac4aed */
+ border: 0.5ch solid var(--theme-tty-border); /* #ac4aed */
background-color: var(--theme-bg);
padding: 20px;
- overflow: hidden;
+ /* overflow: hidden; */
display: flex;
flex-direction: column;
justify-content: start;
align-content: center;
align-items: start;
- animation: kfs-ending 2s 10s forwards;
+ /* animation: kfs-ending 2s 10s forwards; */
}
#typing-prompt {
color: var(--theme-tty-prompt);
- width: 10ch; /* prompt + command length */
- animation: kfs-typing 0.5s steps(4), kfs-cursor-blink 1.2s steps(1, start) 0.6s forwards;
+ /* width: 10ch; /* prompt + command length */ */
+ /* animation: kfs-typing 0.5s steps(4), kfs-cursor-blink 1.2s steps(1, start) 0.6s forwards; */
white-space: nowrap;
- overflow: hidden;
+ /* overflow: hidden; */
border-right: 1ch solid;
margin-bottom: 0.5ch;
}
#typing-result {
- color: var(--theme-tty-warning);
/* "4.8s" means the result is shown 1.8s after typing ends */
- animation: unhide 1s 1.8s forwards;
- visibility: hidden;
+ /* animation: unhide 1s 1.8s forwards; */
+ /* visibility: hidden; */
white-space: nowrap; /* preserve linebreaks */
}
#typing-prompt-segfault {
color: var(--theme-tty-prompt);
- width: 47ch; /* prompt + command length */
+ /* width: 47ch; /* prompt + command length */
/* animation: kfs-typing-segfault 3s steps(36) 2.6s, cursor-blink 0.6s steps(1, start) 3s infinite alternate; */
- animation: kfs-typing-segfault 3s steps(36) 4s forwards, cursor-blink-segfault 0.6s steps(1, start) 7.1s infinite alternate;
+ /* animation: kfs-typing-segfault 3s steps(36) 4s forwards, cursor-blink-segfault 0.6s steps(1, start) 7.1s infinite alternate; */
white-space: nowrap;
- overflow: hidden;
+ /* overflow: hidden; */
border-right: 1ch solid;
margin-bottom: 0.5ch;
- visibility: hidden;
+ /* visibility: hidden; */
}
#typing-result-segfault {
/* "4.8s" means the result is shown 1.8s after typing ends */
- animation: unhide 1s 8.3s forwards;
- visibility: hidden;
+ /* animation: unhide 1s 8.3s forwards; */
+ /* visibility: hidden; */
white-space: nowrap; /* preserve linebreaks */
}
-@keyframes kfs-typing {
- from {
- width: 6ch; /* ignore prompt width */
- }
-}
+/* @keyframes kfs-typing { */
+ /* from { */
+ /* width: 6ch; /* ignore prompt width */ */
+ /* } */
+/* } */
-@keyframes kfs-typing-segfault {
- from {
- width: 11ch; /* ignore prompt width */
- visibility: visible;
- }
- 25% {
- width: 11ch;
- }
- to {
- visibility: visible;
- }
-}
+/* @keyframes kfs-typing-segfault { */
+ /* from { */
+ /* width: 11ch; /* ignore prompt width */ */
+ /* visibility: visible; */
+ /* } */
+ /* 25% { */
+ /* width: 11ch; */
+ /* } */
+ /* to { */
+ /* visibility: visible; */
+ /* } */
+/* } */
-@keyframes kfs-cursor-blink {
- from {
- border-color: transparent;
- }
- 50% {
- border-color: currentColor;
- }
- to {
- border-color: transparent;
- }
-}
+/* @keyframes kfs-cursor-blink { */
+ /* from { */
+ /* border-color: transparent; */
+ /* } */
+ /* 50% { */
+ /* border-color: currentColor; */
+ /* } */
+ /* to { */
+ /* border-color: transparent; */
+ /* } */
+/* } */
-@keyframes cursor-blink-segfault {
- 50% {
- border-color: transparent;
- }
-}
+/* @keyframes cursor-blink-segfault { */
+ /* 50% { */
+ /* border-color: transparent; */
+ /* } */
+/* } */
-@keyframes unhide {
- to {
- visibility: visible;
- }
-}
+/* @keyframes unhide { */
+ /* to { */
+ /* visibility: visible; */
+ /* } */
+/* } */
diff --git a/www/index.html b/www/index.html
index 48d1ebf..166f674 100644
--- a/www/index.html
+++ b/www/index.html
@@ -12,42 +12,54 @@
-
-
+
-
-
-
-
- grub> boot
-
-
-
- ERROR: Root device mounted successfully, but /sbin/init does not exist.
-
-
- Bailing out, you are on your own.
- Good luck
-
-
- sh: can't access tty; job control turned off
-
-
-
-
- [rootfs ]# do butterflies cry when they're sad?
-
-
-
-
- Segmentation fault (core dumped)
-
-
+
+
+
+ grub>
+
+
+
+ boot
+
+
+
+ ERROR: Root device mounted successfully, but /sbin/init does not exist.
+
+
+ Bailing out, you are on your own.
+ Good luck
+
+
+ sh: can't access tty; job control turned off
+
+
+
+
+
+ [rootfs ]#
+
+
+
+ do butterflies cry when they're sad?
+
+
+
+
+ Segmentation fault (core dumped)
+
+
+
+
diff --git a/www/js/main.js b/www/js/main.js
index a5ca67b..da2444c 100644
--- a/www/js/main.js
+++ b/www/js/main.js
@@ -1,24 +1,59 @@
-import { Smc } from "./smc/smc.js"
+import { Smc, fetchShader } from "./smc/lib.js";
+import { PPTY } from "./ppty/lib.js";
main();
-async function fetchShader(uri, delegate) {
- const res = await fetch(uri);
- if (res.ok)
- return await res.text();
- this.raiseError(
- SmcErr.FETCH_SHADER,
- `Failed to load shader source ${url}: ${res.status} ${res.json()}`);
- return ""
+function endBoot(root) {
+ const style = getComputedStyle(root);
+ const paddingLeft = parseFloat(style.paddingLeft);
+ const paddingRight = parseFloat(style.paddingRight);
+ const contentWidth = root.clientWidth - paddingLeft - paddingRight;
+ const paddingTop = parseFloat(style.paddingTop);
+ const paddingBottom = parseFloat(style.paddingBottom);
+ const contentHeight = root.clientHeight - paddingTop - paddingBottom;
+
+ root.style.width = `${contentWidth}px`;
+ root.style.height = `${contentHeight}px`;
+
+ const fade = root.animate(
+ [{ opacity: 0 }],
+ {
+ duration: 400,
+ delay: 3000,
+ fill: "forwards",
+ }
+ );
+
+ fade.onfinish = () => root.remove();
}
-
function main() {
- const canvas = document.querySelector("#gl-canvas");
+ const boot = document.querySelector("#boot-ppty")
+ new PPTY(
+ boot,
+ [
+ {
+ cps: 12,
+ promptDelay: 0.5,
+ commandDelay: 0.7,
+ outputDelay: 1,
+ blinkTime: 0.6,
+ },
+ {
+ cps: 10,
+ promptDelay: 0,
+ commandDelay: 1.5,
+ outputDelay: 1.2,
+ blinkTime: 0.6,
+ }
+ ])
+ .onfinish(endBoot)
+ .run();
+
+ const canvas = document.querySelector("#bg-canvas");
canvas.setAttribute('width', window.innerWidth);
canvas.setAttribute('height', window.innerHeight);
-
fetchShader("../shaders/segfault.glsl")
.then(frag =>
new Smc(canvas)
diff --git a/www/js/main.js.bak b/www/js/main.js.bak
new file mode 100644
index 0000000..2b1eeba
--- /dev/null
+++ b/www/js/main.js.bak
@@ -0,0 +1,133 @@
+import { Smc } from "./smc/lib.js"
+
+main();
+
+async function fetchShader(uri, delegate) {
+ const res = await fetch(uri);
+ if (res.ok)
+ return await res.text();
+ this.raiseError(
+ SmcErr.FETCH_SHADER,
+ `Failed to load shader source ${url}: ${res.status} ${res.json()}`);
+ return ""
+}
+
+
+function animDelta(anim) {
+ const timing = anim.effect.getComputedTiming();
+ return (timing.delay ?? 0) + (timing.duration ?? 0) + (timing.endDelay ?? 0);
+}
+
+function main() {
+ const cps = 12; // chars per second
+ const promptDelay = 0.5;
+ const commandDelay = 5;
+ const outputDelay = 5;
+ const blinkTime = 600; // x2 then x1000
+
+ // WARNING: delay does not account for runtime
+ // WARNING: it assumes all computations are down instantaneously
+ var delay = 0;
+
+ const boot = document.querySelector("#boot-ppty");
+ // WARNING: ensure boot != null
+ boot
+ .querySelectorAll(".ppty-block")
+ .forEach((block, index, blocks) => {
+ const prompt = block.querySelector(".ppty-prompt");
+ const command = block.querySelector(".ppty-command");
+ const output = block.querySelector(".ppty-output");
+
+ // WARNING: ensure prompt|command|output != null
+ const promptAnim = prompt.animate(
+ [{ visibility: "visible" }],
+ {
+ delay: delay + promptDelay * 1000,
+ fill: "forwards",
+ },
+ );
+ delay = animDelta(promptAnim);
+
+ const showCursor = () => { command.style.borderRightColor = cursorColor; return true; };
+ const hideCursor = () => { command.style.borderRightColor = "transparent"; return false; };
+ const startCursorBlink = () => setInterval(() => {
+ cursorVisible = cursorVisible ? hideCursor() : showCursor();
+ }, blinkTime);
+
+ const cursorColor = command.style.borderRightColor;
+ var cursorVisible = true;
+ var blinkId = null;
+ setTimeout(() => {
+ command.style.visibility = "visible";
+ blinkId = startCursorBlink();
+ }, delay);
+
+ // WARNING: ensure command.textContent != null
+ const commandLen = command.textContent.trim().length
+ const commandAnim = command.animate(
+ [
+ {
+ width: "0ch",
+ visibility: "visible",
+ },
+ {
+ width: `${commandLen}ch`,
+ visibility: "visible",
+ }
+ ],
+ {
+ duration: commandLen / cps * 1000,
+ delay: delay + commandDelay * 1000,
+ easing: `steps(${commandLen}, end)`,
+ fill: "forwards",
+ }
+ );
+ // pause the cursor while typing
+ setTimeout(() => {
+ clearInterval(blinkId);
+ showCursor();
+ cursorVisible = true;
+ }, delay + commandDelay * 1000);
+ delay = animDelta(commandAnim);
+ setTimeout(() => {
+ blinkId = startCursorBlink();
+ }, delay);
+
+
+ setTimeout(() => {
+ clearInterval(blinkId);
+ hideCursor();
+ }, delay + outputDelay * 1000);
+
+ // unhide output
+ output.animate(
+ [{ visibility: "visible" }],
+ {
+ delay: delay + outputDelay * 1000,
+ fill: "forwards",
+ }
+ );
+ delay += outputDelay * 1000;
+
+ // hide the cursor after output displays
+ setTimeout(() => command.style.borderRightColor = "transparent", delay);
+ });
+
+
+
+ const canvas = document.querySelector("#bg-canvas");
+ canvas.setAttribute('width', window.innerWidth);
+ canvas.setAttribute('height', window.innerHeight);
+
+
+ fetchShader("../shaders/segfault.glsl")
+ .then(frag =>
+ new Smc(canvas)
+ .setMaxFps(30)
+ .setProgram(builder =>
+ builder
+ .addFragmentShader(frag))
+ .run()
+ );
+}
+
diff --git a/www/js/ppty/lib.js b/www/js/ppty/lib.js
new file mode 100644
index 0000000..2646e23
--- /dev/null
+++ b/www/js/ppty/lib.js
@@ -0,0 +1,2 @@
+import { PPTY } from "./ppty.js";
+export { PPTY };
diff --git a/www/js/ppty/ppty.js b/www/js/ppty/ppty.js
new file mode 100644
index 0000000..a471e3f
--- /dev/null
+++ b/www/js/ppty/ppty.js
@@ -0,0 +1,130 @@
+export { PPTY };
+
+function animDelta(anim) {
+ const timing = anim.effect.getComputedTiming();
+ return (timing.delay ?? 0) + (timing.duration ?? 0) + (timing.endDelay ?? 0);
+}
+
+class PPTY {
+ #root;
+ #blockOptions;
+ #startCallback = _ => { };
+ #finishCallback = _ => { };
+
+ constructor(root, blockOptions) {
+ this.#root = root;
+ this.#blockOptions = blockOptions;
+ }
+
+ onstart(handler) {
+ this.#startCallback = handler;
+ return this;
+ }
+
+ onfinish(handler) {
+ this.#finishCallback = handler;
+ return this;
+ }
+
+ run() {
+ var delay = 0;
+
+ this.#startCallback(this.#root);
+
+ this.#root
+ .querySelectorAll(".ppty-block")
+ .forEach((block, index, blocks) => {
+ const prompt = block.querySelector(".ppty-prompt");
+ const command = block.querySelector(".ppty-command");
+ const output = block.querySelector(".ppty-output");
+
+ const options = this.#blockOptions[index];
+ const cps = options["cps"]; // chars per second
+ const promptDelay = options["promptDelay"] * 1000;
+ const commandDelay = options["commandDelay"] * 1000;
+ const outputDelay = options["outputDelay"] * 1000;
+ const blinkTime = options["blinkTime"] * 2 * 1000; // x2 then x1000
+
+ // WARNING: ensure prompt|command|output != null
+ const promptAnim = prompt.animate(
+ [{ visibility: "visible" }],
+ {
+ delay: delay + promptDelay,
+ fill: "forwards",
+ },
+ );
+ delay = animDelta(promptAnim);
+
+ const showCursor = () => { command.style.borderRightColor = cursorColor; return true; };
+ const hideCursor = () => { command.style.borderRightColor = "transparent"; return false; };
+ const startCursorBlink = () => setInterval(() => {
+ cursorVisible = cursorVisible ? hideCursor() : showCursor();
+ }, blinkTime);
+
+ const cursorColor = command.style.borderRightColor;
+ var cursorVisible = true;
+ var blinkId = null;
+ setTimeout(() => {
+ command.style.visibility = "visible";
+ blinkId = startCursorBlink();
+ }, delay);
+
+ // WARNING: ensure command.textContent != null
+ const commandLen = command.textContent.trim().length
+ const commandAnim = command.animate(
+ [
+ {
+ width: "0ch",
+ visibility: "visible",
+ },
+ {
+ width: `${commandLen}ch`,
+ visibility: "visible",
+ }
+ ],
+ {
+ duration: commandLen / cps * 1000,
+ delay: delay + commandDelay,
+ easing: `steps(${commandLen}, end)`,
+ fill: "forwards",
+ }
+ );
+ // pause the cursor while typing
+ setTimeout(() => {
+ clearInterval(blinkId);
+ showCursor();
+ cursorVisible = true;
+ }, delay + commandDelay);
+ delay = animDelta(commandAnim);
+ setTimeout(() => {
+ blinkId = startCursorBlink();
+ }, delay);
+
+ // delay until output is visible
+ delay += outputDelay;
+ setTimeout(() => {
+ clearInterval(blinkId);
+ hideCursor();
+ }, delay);
+
+ // unhide output
+ output.animate(
+ [{ visibility: "visible" }],
+ {
+ delay: delay,
+ fill: "forwards",
+ }
+ );
+
+ // hide the cursor after output displays and run the callback
+ setTimeout(() => {
+ command.style.borderRightColor = "transparent";
+ if (index == blocks.length - 1)
+ this.#finishCallback(this.#root);
+ }, delay);
+ });
+ }
+}
+
+
+
diff --git a/www/js/smc/lib.js b/www/js/smc/lib.js
index cbff55e..0635b4c 100644
--- a/www/js/smc/lib.js
+++ b/www/js/smc/lib.js
@@ -1,4 +1,14 @@
import { Smc } from "./smc.js";
import { SmcErr } from "./errors.js";
-export { Smc, SmcErr };
+export { Smc, SmcErr, fetchShader };
+
+async function fetchShader(uri, delegate) {
+ const res = await fetch(uri);
+ if (res.ok)
+ return await res.text();
+ this.raiseError(
+ SmcErr.FETCH_SHADER,
+ `Failed to load shader source ${url}: ${res.status} ${res.json()}`);
+ return ""
+}
diff --git a/www/js/smc/smc.js b/www/js/smc/smc.js
index 9829921..f81b8cb 100644
--- a/www/js/smc/smc.js
+++ b/www/js/smc/smc.js
@@ -164,10 +164,11 @@ class Smc {
var delta = time - this.#prevTimeMs;
this.render(time, delta);
- setTimeout(
- () => requestAnimationFrame(this.renderLoop),
- Math.max(0, delta - this.#minDeltaTimeMs)
- );
+ requestAnimationFrame(this.renderLoop);
+ // setTimeout(
+ // () => requestAnimationFrame(this.renderLoop),
+ // Math.max(0, delta - this.#minDeltaTimeMs)
+ // );
this.#prevTimeMs = time;
}
diff --git a/www/shaders/segfault.glsl b/www/shaders/segfault.glsl
index a96bb19..463fb45 100644
--- a/www/shaders/segfault.glsl
+++ b/www/shaders/segfault.glsl
@@ -15,9 +15,8 @@ uniform float uTime;
uniform vec2 uResolution;
/* ==== Text Colouring ==== */
-#define PHOSPHOR_COL vec4(196./255., 167./255., 231./255., 1.)
-// #define BG_COL vec4(0.2, 0.0, 0.2, 0.5)
-#define BG_COL vec4(14./255., 13./255., 20./255., 1.)
+#define PHOSPHOR_COL vec4(224./255., 222./255., 244./255., 1.)
+#define BG_COL vec4(24./255., 23./255., 30./255., 1.)
/* ======= Text Size ======= */
#define FONT_SIZE vec2(10.,20.)
#define ROWCOLS vec2(80., 24.)