summaryrefslogtreecommitdiff
path: root/public
diff options
context:
space:
mode:
Diffstat (limited to 'public')
-rw-r--r--public/shell.js73
-rw-r--r--public/styles/fonts/LowresPixel-Regular.woffbin0 -> 10756 bytes
-rw-r--r--public/styles/fonts/LowresPixel.css8
-rw-r--r--public/styles/fonts/Monocraft.css18
-rw-r--r--public/styles/main.css14
-rw-r--r--public/styles/style.css167
6 files changed, 280 insertions, 0 deletions
diff --git a/public/shell.js b/public/shell.js
new file mode 100644
index 0000000..9a70846
--- /dev/null
+++ b/public/shell.js
@@ -0,0 +1,73 @@
+const shell = document.querySelector("[data-shell]");
+
+if (shell) {
+ const area = shell.closest("[data-scroll-area]");
+ const history = shell.querySelector("#shell-history");
+ const input = shell.querySelector("[data-shell-input]");
+ const caret = shell.querySelector("[data-shell-caret]");
+ let busy = false;
+
+ const updateCaret = () => {
+ const pos = input.selectionStart ?? input.value.length;
+ caret.style.left = `calc(${pos}ch - ${input.scrollLeft}px)`;
+ caret.textContent = input.value[pos] ?? "\u00a0";
+ caret.style.animation = "none"; // restart the blink so the caret is solid while typing
+ void caret.offsetWidth;
+ caret.style.animation = "";
+ };
+
+ const snapToBottom = () => {
+ area.scrollTop = area.scrollHeight;
+ };
+
+ const run = async (cmd) => {
+ busy = true;
+ try {
+ const res = await fetch("/shell/run", {
+ method: "POST",
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
+ body: new URLSearchParams({ cmd }),
+ });
+ const data = await res.json();
+ if (data.clear) history.replaceChildren();
+ if (data.html) history.insertAdjacentHTML("beforeend", data.html);
+ if (data.vars) {
+ for (const [key, value] of Object.entries(data.vars)) {
+ document.documentElement.style.setProperty(`--${key}`, value);
+ }
+ }
+ } catch {
+ const el = document.createElement("p");
+ el.textContent = "request failed";
+ history.append(el);
+ } finally {
+ busy = false;
+ snapToBottom();
+ }
+ };
+
+ input.addEventListener("keydown", (e) => {
+ if (e.key !== "Enter") return;
+ e.preventDefault();
+ const cmd = input.value.trim();
+ if (!cmd || busy) return;
+ input.value = "";
+ updateCaret();
+ run(cmd);
+ });
+
+ for (const event of ["input", "keyup", "click", "focus"]) {
+ input.addEventListener(event, updateCaret);
+ }
+ document.addEventListener("selectionchange", () => {
+ if (document.activeElement === input) updateCaret();
+ });
+
+ area.addEventListener("click", () => {
+ if (!getSelection().toString()) input.focus({ preventScroll: true });
+ });
+
+ input.focus({ preventScroll: true });
+ updateCaret();
+ snapToBottom();
+}
diff --git a/public/styles/fonts/LowresPixel-Regular.woff b/public/styles/fonts/LowresPixel-Regular.woff
new file mode 100644
index 0000000..a1b368a
--- /dev/null
+++ b/public/styles/fonts/LowresPixel-Regular.woff
Binary files differ
diff --git a/public/styles/fonts/LowresPixel.css b/public/styles/fonts/LowresPixel.css
new file mode 100644
index 0000000..ae4f1d3
--- /dev/null
+++ b/public/styles/fonts/LowresPixel.css
@@ -0,0 +1,8 @@
+@font-face {
+ font-family: "LowerPixelRegular";
+ font-style: normal;
+ font-weight: normal;
+ src:
+ local("LowerPixelRegular"),
+ url("LowresPixel-Regular.woff") format("woff");
+}
diff --git a/public/styles/fonts/Monocraft.css b/public/styles/fonts/Monocraft.css
index df2874e..af7b397 100644
--- a/public/styles/fonts/Monocraft.css
+++ b/public/styles/fonts/Monocraft.css
@@ -3,106 +3,124 @@
src: url("/public/fonts/Monocraft-ExtraLight.ttf") format("truetype");
font-weight: 100;
font-style: normal;
+ size-adjust: 90%; /* Adjust to JetBrainsMono width, 0.6 / 0.6666 = 0.9 */
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-ExtraLight-Italic.ttf") format("truetype");
font-weight: 100;
font-style: italic;
+ size-adjust: 90%; /* Adjust to JetBrainsMono width, 0.6 / 0.6666 = 0.9 */
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-ExtraLight.ttf") format("truetype");
font-weight: 200;
font-style: normal;
+ size-adjust: 90%; /* Adjust to JetBrainsMono width, 0.6 / 0.6666 = 0.9 */
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-ExtraLight-Italic.ttf") format("truetype");
font-weight: 200;
font-style: italic;
+ size-adjust: 90%; /* Adjust to JetBrainsMono width, 0.6 / 0.6666 = 0.9 */
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Light.ttf") format("truetype");
font-weight: 300;
font-style: normal;
+ size-adjust: 90%; /* Adjust to JetBrainsMono width, 0.6 / 0.6666 = 0.9 */
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Light-Italic.ttf") format("truetype");
font-weight: 300;
font-style: italic;
+ size-adjust: 90%; /* Adjust to JetBrainsMono width, 0.6 / 0.6666 = 0.9 */
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft.ttf") format("truetype");
font-weight: 400;
font-style: normal;
+ size-adjust: 90%; /* Adjust to JetBrainsMono width, 0.6 / 0.6666 = 0.9 */
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Italic.ttf") format("truetype");
font-weight: 400;
font-style: italic;
+ size-adjust: 90%; /* Adjust to JetBrainsMono width, 0.6 / 0.6666 = 0.9 */
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft.ttf") format("truetype");
font-weight: 500;
font-style: normal;
+ size-adjust: 90%; /* Adjust to JetBrainsMono width, 0.6 / 0.6666 = 0.9 */
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Italic.ttf") format("truetype");
font-weight: 500;
font-style: italic;
+ size-adjust: 90%; /* Adjust to JetBrainsMono width, 0.6 / 0.6666 = 0.9 */
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-SemiBold.ttf") format("truetype");
font-weight: 600;
font-style: normal;
+ size-adjust: 90%; /* Adjust to JetBrainsMono width, 0.6 / 0.6666 = 0.9 */
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-SemiBold-Italic.ttf") format("truetype");
font-weight: 600;
font-style: italic;
+ size-adjust: 90%; /* Adjust to JetBrainsMono width, 0.6 / 0.6666 = 0.9 */
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Bold.ttf") format("truetype");
font-weight: 700;
font-style: normal;
+ size-adjust: 90%; /* Adjust to JetBrainsMono width, 0.6 / 0.6666 = 0.9 */
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Bold-Italic.ttf") format("truetype");
font-weight: 700;
font-style: italic;
+ size-adjust: 90%; /* Adjust to JetBrainsMono width, 0.6 / 0.6666 = 0.9 */
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Black.ttf") format("truetype");
font-weight: 800;
font-style: normal;
+ size-adjust: 90%; /* Adjust to JetBrainsMono width, 0.6 / 0.6666 = 0.9 */
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Black-Italic.ttf") format("truetype");
font-weight: 800;
font-style: italic;
+ size-adjust: 90%; /* Adjust to JetBrainsMono width, 0.6 / 0.6666 = 0.9 */
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Black.ttf") format("truetype");
font-weight: 900;
font-style: normal;
+ size-adjust: 90%; /* Adjust to JetBrainsMono width, 0.6 / 0.6666 = 0.9 */
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Black-Italic.ttf") format("truetype");
font-weight: 900;
font-style: italic;
+ size-adjust: 90%; /* Adjust to JetBrainsMono width, 0.6 / 0.6666 = 0.9 */
}
diff --git a/public/styles/main.css b/public/styles/main.css
index 2c44266..f7539ee 100644
--- a/public/styles/main.css
+++ b/public/styles/main.css
@@ -1,6 +1,7 @@
@import "./fonts/Inter/inter.css";
@import "./fonts/JetBrainsMono.css";
@import "./fonts/Monocraft.css";
+@import "./fonts/LowresPixel.css";
@layer theme, base, components, utilities;
@import "tailwindcss/theme.css" layer(theme);
@@ -260,3 +261,16 @@ body {
transform: scale(1.03);
}
}
+
+@keyframes blink {
+ 50% {
+ opacity: 0;
+ }
+}
+
+[data-shell-caret] {
+ animation: blink 1.1s steps(1) infinite;
+}
+[data-shell-prompt]:not(:focus-within) [data-shell-caret] {
+ visibility: hidden;
+}
diff --git a/public/styles/style.css b/public/styles/style.css
index 4aff75b..3610b64 100644
--- a/public/styles/style.css
+++ b/public/styles/style.css
@@ -523,108 +523,132 @@
src: url("/public/fonts/Monocraft-ExtraLight.ttf") format("truetype");
font-weight: 100;
font-style: normal;
+ size-adjust: 90%;
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-ExtraLight-Italic.ttf") format("truetype");
font-weight: 100;
font-style: italic;
+ size-adjust: 90%;
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-ExtraLight.ttf") format("truetype");
font-weight: 200;
font-style: normal;
+ size-adjust: 90%;
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-ExtraLight-Italic.ttf") format("truetype");
font-weight: 200;
font-style: italic;
+ size-adjust: 90%;
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Light.ttf") format("truetype");
font-weight: 300;
font-style: normal;
+ size-adjust: 90%;
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Light-Italic.ttf") format("truetype");
font-weight: 300;
font-style: italic;
+ size-adjust: 90%;
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft.ttf") format("truetype");
font-weight: 400;
font-style: normal;
+ size-adjust: 90%;
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Italic.ttf") format("truetype");
font-weight: 400;
font-style: italic;
+ size-adjust: 90%;
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft.ttf") format("truetype");
font-weight: 500;
font-style: normal;
+ size-adjust: 90%;
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Italic.ttf") format("truetype");
font-weight: 500;
font-style: italic;
+ size-adjust: 90%;
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-SemiBold.ttf") format("truetype");
font-weight: 600;
font-style: normal;
+ size-adjust: 90%;
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-SemiBold-Italic.ttf") format("truetype");
font-weight: 600;
font-style: italic;
+ size-adjust: 90%;
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Bold.ttf") format("truetype");
font-weight: 700;
font-style: normal;
+ size-adjust: 90%;
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Bold-Italic.ttf") format("truetype");
font-weight: 700;
font-style: italic;
+ size-adjust: 90%;
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Black.ttf") format("truetype");
font-weight: 800;
font-style: normal;
+ size-adjust: 90%;
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Black-Italic.ttf") format("truetype");
font-weight: 800;
font-style: italic;
+ size-adjust: 90%;
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Black.ttf") format("truetype");
font-weight: 900;
font-style: normal;
+ size-adjust: 90%;
}
@font-face {
font-family: "Monocraft";
src: url("/public/fonts/Monocraft-Black-Italic.ttf") format("truetype");
font-weight: 900;
font-style: italic;
+ size-adjust: 90%;
+}
+@font-face {
+ font-family: "LowerPixelRegular";
+ font-style: normal;
+ font-weight: normal;
+ src: local("LowerPixelRegular"), url("LowresPixel-Regular.woff") format("woff");
}
@layer theme, base, components, utilities;
@layer theme {
@@ -796,12 +820,29 @@
}
}
@layer utilities {
+ .pointer-events-none {
+ pointer-events: none;
+ }
+ .collapse {
+ visibility: collapse;
+ }
.invisible {
visibility: hidden;
}
.visible {
visibility: visible;
}
+ .sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip-path: inset(50%);
+ white-space: nowrap;
+ border-width: 0;
+ }
.absolute {
position: absolute;
}
@@ -817,6 +858,12 @@
.inset-x-\[0\.3rem\] {
inset-inline: 0.3rem;
}
+ .inset-y-0 {
+ inset-block: 0px;
+ }
+ .top-1 {
+ top: var(--spacing);
+ }
.top-1\/2 {
top: calc(1 / 2 * 100%);
}
@@ -856,6 +903,24 @@
.col-span-2 {
grid-column: span 2 / span 2;
}
+ .container {
+ width: 100%;
+ @media (width >= 40rem) {
+ max-width: 40rem;
+ }
+ @media (width >= 48rem) {
+ max-width: 48rem;
+ }
+ @media (width >= 64rem) {
+ max-width: 64rem;
+ }
+ @media (width >= 80rem) {
+ max-width: 80rem;
+ }
+ @media (width >= 96rem) {
+ max-width: 96rem;
+ }
+ }
.-m-\[0\.4rem\] {
margin: calc(0.4rem * -1);
}
@@ -1376,6 +1441,9 @@
.hidden {
display: none;
}
+ .inline {
+ display: inline;
+ }
.inline-block {
display: inline-block;
}
@@ -1427,6 +1495,9 @@
.w-8 {
width: calc(var(--spacing) * 8);
}
+ .w-\[1ch\] {
+ width: 1ch;
+ }
.w-\[30\%\] {
width: 30%;
}
@@ -1460,9 +1531,25 @@
.flex-1 {
flex: 1;
}
+ .flex-shrink {
+ flex-shrink: 1;
+ }
.shrink-0 {
flex-shrink: 0;
}
+ .flex-grow {
+ flex-grow: 1;
+ }
+ .grow {
+ flex-grow: 1;
+ }
+ .border-collapse {
+ border-collapse: collapse;
+ }
+ .-translate-y-1 {
+ --tw-translate-y: calc(var(--spacing) * -1);
+ translate: var(--tw-translate-x) var(--tw-translate-y);
+ }
.-translate-y-1\/2 {
--tw-translate-y: calc(calc(1 / 2 * 100%) * -1);
translate: var(--tw-translate-x) var(--tw-translate-y);
@@ -1542,6 +1629,9 @@
.gap-\[1ch\] {
gap: 1ch;
}
+ .gap-\[1rem\] {
+ gap: 1rem;
+ }
.gap-x-4 {
column-gap: calc(var(--spacing) * 4);
}
@@ -1684,12 +1774,18 @@
.bg-\(--secondary\) {
background-color: var(--secondary);
}
+ .bg-\(--text\) {
+ background-color: var(--text);
+ }
.bg-\[var\(--background\)\] {
background-color: var(--background);
}
.bg-\[var\(--text\)\] {
background-color: var(--text);
}
+ .bg-black {
+ background-color: var(--color-black);
+ }
.bg-black\/20 {
background-color: color-mix(in srgb, #000 20%, transparent);
@supports (color: color-mix(in lab, red, red)) {
@@ -1702,6 +1798,12 @@
background-color: color-mix(in oklab, var(--color-black) 30%, transparent);
}
}
+ .bg-transparent {
+ background-color: transparent;
+ }
+ .bg-white {
+ background-color: var(--color-white);
+ }
.bg-white\/2 {
background-color: color-mix(in srgb, #fff 2%, transparent);
@supports (color: color-mix(in lab, red, red)) {
@@ -1720,6 +1822,9 @@
.bg-\[linear-gradient\(to_bottom\,var\(--background\)\,var\(--cmp-bg\,transparent\)\)\] {
background-image: linear-gradient(to bottom,var(--background),var(--cmp-bg,transparent));
}
+ .mask-repeat {
+ mask-repeat: repeat;
+ }
.fill-\[\#7ed4fb\]\/50 {
fill: color-mix(in oklab, #7ed4fb 50%, transparent);
}
@@ -1759,6 +1864,9 @@
.p-\[3vw\] {
padding: 3vw;
}
+ .px-3 {
+ padding-inline: calc(var(--spacing) * 3);
+ }
.px-3\.5 {
padding-inline: calc(var(--spacing) * 3.5);
}
@@ -1892,6 +2000,10 @@
--tw-leading: 1.7;
line-height: 1.7;
}
+ .leading-\[1\.32\] {
+ --tw-leading: 1.32;
+ line-height: 1.32;
+ }
.leading-\[normal\] {
--tw-leading: normal;
line-height: normal;
@@ -1914,6 +2026,9 @@
.whitespace-nowrap {
white-space: nowrap;
}
+ .whitespace-pre {
+ white-space: pre;
+ }
.text-\(--accent\) {
color: var(--accent);
}
@@ -1992,6 +2107,9 @@
.text-\[\#a34eeb\] {
color: #a34eeb;
}
+ .text-\[\#ffffff\] {
+ color: #ffffff;
+ }
.text-\[\%v\] {
color: %v;
}
@@ -2004,9 +2122,18 @@
.text-\[var\(--secondary\)\] {
color: var(--secondary);
}
+ .italic {
+ font-style: italic;
+ }
.no-underline {
text-decoration-line: none;
}
+ .underline {
+ text-decoration-line: underline;
+ }
+ .caret-transparent {
+ caret-color: transparent;
+ }
.opacity-0 {
opacity: 0%;
}
@@ -2017,6 +2144,19 @@
}
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
+ .ring {
+ --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
+ }
+ .outline {
+ outline-style: var(--tw-outline-style);
+ outline-width: 1px;
+ }
+ .transition {
+ transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events;
+ transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
+ transition-duration: var(--tw-duration, var(--default-transition-duration));
+ }
.transition-colors {
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
@@ -2026,6 +2166,10 @@
--tw-duration: 150ms;
transition-duration: 150ms;
}
+ .outline-none {
+ --tw-outline-style: none;
+ outline-style: none;
+ }
.select-text {
-webkit-user-select: text;
user-select: text;
@@ -2071,6 +2215,12 @@
background-color: color-mix(in oklab, var(--primary) 25%, transparent) !important;
}
}
+ .group-data-\[copied\]\/copy\:block:is(:where(.group\/copy)[data-copied] *) {
+ display: block;
+ }
+ .group-data-\[copied\]\/copy\:hidden:is(:where(.group\/copy)[data-copied] *) {
+ display: none;
+ }
.after\:absolute::after {
content: var(--tw-content);
position: absolute;
@@ -2487,6 +2637,17 @@ body {
transform: scale(1.03);
}
}
+@keyframes blink {
+ 50% {
+ opacity: 0;
+ }
+}
+[data-shell-caret] {
+ animation: blink 1.1s steps(1) infinite;
+}
+[data-shell-prompt]:not(:focus-within) [data-shell-caret] {
+ visibility: hidden;
+}
@property --tw-translate-x {
syntax: "*";
inherits: false;
@@ -2620,6 +2781,11 @@ body {
inherits: false;
initial-value: 0 0 #0000;
}
+@property --tw-outline-style {
+ syntax: "*";
+ inherits: false;
+ initial-value: solid;
+}
@property --tw-duration {
syntax: "*";
inherits: false;
@@ -2661,6 +2827,7 @@ body {
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-offset-shadow: 0 0 #0000;
+ --tw-outline-style: solid;
--tw-duration: initial;
--tw-content: "";
}