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.)