WIP: vim emulation

This commit is contained in:
2024-05-15 00:51:50 +01:00
parent 8be90fe64e
commit c95923d30f
12 changed files with 263 additions and 31 deletions

View File

@@ -0,0 +1,117 @@
<div id="command-line" class="w-full flex flex-row bg-base">
<input
id="command-line-input"
type="text"
size="1"
class="bg-base focus:outline-none active:outline-none cursor-default caret-transparent"
/>
<div id="caret" aria-hidden="true" class="bg-text inline-block">&nbsp;</div>
<p id="status-text" class="absolute w-full h-full bg-base hidden italic">
test
</p>
</div>
<script>
const cmds: Record<string, () => void> = {
q: () => {
document.body.style.visibility = "hidden";
},
};
const container = document.getElementById("command-line") as HTMLDivElement;
const commandLineInput = document.getElementById(
"command-line-input",
) as HTMLInputElement;
const caret = document.getElementById("caret") as HTMLDivElement;
const statusText = document.getElementById(
"status-text",
) as HTMLParagraphElement;
const allowedCharRegex = /[\w\d\s!@#$%^&*()-_+=|\\~`{[}\]:;"'<,>.?/]/;
let isInCmdMode = false;
commandLineInput.value = "";
caret.style.display = "none";
function enableCmdMode() {
caret.style.display = "block";
statusText.classList.add("hidden");
commandLineInput.size = 1;
commandLineInput.value = ":";
isInCmdMode = true;
const event = new Event("cmdmodeenabled");
container.dispatchEvent(event);
}
function disableCmdMode({ clear } = { clear: true }) {
caret.style.display = "none";
isInCmdMode = false;
if (clear) {
commandLineInput.size = 1;
commandLineInput.value = "";
}
const event = new Event("cmdmodedisabled");
container.dispatchEvent(event);
}
function appendChar(event: KeyboardEvent) {
commandLineInput.value += event.key;
commandLineInput.size += 1;
}
function executeCommand() {
disableCmdMode({ clear: false });
const command = commandLineInput.value.substring(1);
const fn = cmds[command];
if (fn) {
fn();
} else {
statusText.innerHTML = `E492: Not an editor command: ${command}`;
statusText.classList.remove("hidden");
statusText.classList.add("text-red");
}
}
document.addEventListener("keydown", (event) => {
switch (event.key) {
case ":":
if (!isInCmdMode) {
enableCmdMode();
} else {
appendChar(event);
}
break;
case "Escape":
disableCmdMode();
break;
case "Backspace":
commandLineInput.value = commandLineInput.value.substring(
0,
commandLineInput.value.length - 1,
);
commandLineInput.size--;
if (commandLineInput.value === "") {
disableCmdMode();
}
break;
case "Enter":
executeCommand();
break;
default:
if (
event.key.length === 1 &&
allowedCharRegex.test(event.key) &&
isInCmdMode
) {
appendChar(event);
}
break;
}
});
</script>

View File

@@ -0,0 +1,44 @@
---
import Link from "./Link.astro";
import CommandLine from "./CommandLine.astro";
---
<div class="absolute w-full bottom-0">
<footer class="w-full bg-crust flex flex-row leading-tight">
<span
id="status-indicator"
class="bg-blue text-base inline-block leading-tight"
>&nbsp;NORMAL&nbsp;</span
>
<div class="flex flex-row bg-surface0">
<span>&nbsp;</span>
<Link href="https://github.com/kennethnym">github</Link>
<span>&nbsp;</span>
<Link href="https://x.com/kennethnym">x.com</Link>
<span>&nbsp;</span>
<Link href="mailto:kennethnym@outlook.com">email</Link>
<span>&nbsp;</span>
</div>
</footer>
<CommandLine />
</div>
<script>
const statusIndicator = document.getElementById(
"status-indicator",
) as HTMLSpanElement;
const cmdLine = document.getElementById("command-line") as HTMLDivElement;
cmdLine.addEventListener("cmdmodeenabled", () => {
statusIndicator.innerHTML = "&nbsp;COMMAND&nbsp;";
statusIndicator.classList.remove("bg-blue");
statusIndicator.classList.add("bg-peach");
console.log("cmd mode!");
});
cmdLine.addEventListener("cmdmodedisabled", () => {
statusIndicator.innerHTML = "&nbsp;NORMAL&nbsp;";
statusIndicator.classList.add("bg-blue");
statusIndicator.classList.remove("bg-peach");
});
</script>

View File

@@ -10,6 +10,7 @@ type Props = CollectionEntry<"blog">["data"];
const { title, description, pubDate, updatedDate, heroImage } = Astro.props;
---
<!doctype html>
<html lang="en" class="latte dark:mocha">
<head>
<BaseHead title={title} description={description} image={heroImage} />
@@ -41,5 +42,5 @@ const { title, description, pubDate, updatedDate, heroImage } = Astro.props;
</main>
<hr class="py-4" />
<Footer />
</Body>
</body>
</html>

View File

@@ -1,14 +1,15 @@
---
import BaseHead from "../components/BaseHead.astro";
import Footer from "../components/Footer.astro";
import { SITE_TITLE, SITE_DESCRIPTION } from "../consts";
import Link from "../components/Link.astro";
import FormattedDate from "../components/FormattedDate.astro";
import LuaLine from "../components/LuaLine.astro";
import { getBlogs } from "../content/blog";
const posts = (await getBlogs()).sort(
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
);
const currentMonth = new Date().getMonth();
---
<!doctype html>
@@ -17,34 +18,20 @@ const posts = (await getBlogs()).sort(
<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
</head>
<body
class="bg-base text-text max-w-4xl m-auto p-8"
class="bg-base text-text h-screen m-auto p-8 flex items-center justify-center overflow-hidden"
>
<header>
<p class="font-bold text-2xl">kennethnym</p>
</header>
<main class="py-8">
<p>dumping ground for my thoughts. all opinions are my own.</p>
<h1 class="font-bold mt-8 mb-2 text-lg visited">current projects:</h1>
<ul class="not-prose space-y-4 md:space-y-2">
<li>
<Link href="https://polygui.org">poly</Link>: a language-agnostic,
cross-platform GUI framework for building OS-native applications.
</li>
<li>
<Link href="https://polygui.org/nanopack/introduction/">nanopack</Link
>: a zero-runtime, type-safe binary serialization format.
</li>
<li>
<Link href="https://github.com/kennethnym/StarlightLauncher"
>starlight launcher</Link
>: an open-source android launcher with a search-based ui.
</li>
</ul>
<h1 class="font-bold mt-8 mb-2 text-lg visited">my thoughts:</h1>
<ul>
<main
class="py-8 max-w-2xl flex flex-col items-center space-y-0 leading-tight"
>
<p>KENNETHNYM v23.{currentMonth + 4}</p>
<p class="leading-none">&nbsp;</p>
<p>software engineer. unpaid hhkb salesman.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<ul class="w-full">
{
posts.map((post) => (
<li class="flex flex-row justify-between hover:bg-opacity-10 hover:bg-text -mx-2 px-2 rounded">
<li class="flex flex-row justify-between hover:bg-opacity-10 hover:bg-text -mx-2 px-2 rounded space-x-8">
<Link href={`/blog/${post.slug}`}>{post.data.title}</Link>
<FormattedDate date={post.data.pubDate} />
</li>
@@ -52,7 +39,49 @@ const posts = (await getBlogs()).sort(
}
</ul>
</main>
<hr class="py-2" />
<Footer />
<div class="absolute w-full h-full px-2 -z-10 leading-tight">
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
<p>~</p>
</div>
<LuaLine client:load />
</body>
</html>

View File

@@ -1,3 +1,43 @@
body {
font-family: monospace, monospace;
@font-face {
font-family: "CommitMono";
src: url("/fonts/CommitMono-400-Regular.otf") format("opentype");
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "CommitMono";
src: url("/fonts/CommitMono-400-Italic.otf") format("opentype");
font-weight: normal;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "CommitMono";
src: url("/fonts/CommitMono-700-Regular.otf") format("opentype");
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "CommitMono";
src: url("/fonts/CommitMono-700-Italic.otf") format("opentype");
font-weight: 700;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "NerdFont";
src: url("/fonts/SymbolsNerdFontMono-Regular.ttf") format("truetype");
font-weight: normal;
font-style: normal;
font-display: swap;
}
body {
font-family: "CommitMono", monospace, monospace;
}
.nf {
font-family: "NerdFont";
}