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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 483 KiB

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; const { title, description, pubDate, updatedDate, heroImage } = Astro.props;
--- ---
<!doctype html>
<html lang="en" class="latte dark:mocha"> <html lang="en" class="latte dark:mocha">
<head> <head>
<BaseHead title={title} description={description} image={heroImage} /> <BaseHead title={title} description={description} image={heroImage} />
@@ -41,5 +42,5 @@ const { title, description, pubDate, updatedDate, heroImage } = Astro.props;
</main> </main>
<hr class="py-4" /> <hr class="py-4" />
<Footer /> <Footer />
</Body> </body>
</html> </html>

View File

@@ -1,14 +1,15 @@
--- ---
import BaseHead from "../components/BaseHead.astro"; import BaseHead from "../components/BaseHead.astro";
import Footer from "../components/Footer.astro";
import { SITE_TITLE, SITE_DESCRIPTION } from "../consts"; import { SITE_TITLE, SITE_DESCRIPTION } from "../consts";
import Link from "../components/Link.astro"; import Link from "../components/Link.astro";
import FormattedDate from "../components/FormattedDate.astro"; import FormattedDate from "../components/FormattedDate.astro";
import LuaLine from "../components/LuaLine.astro";
import { getBlogs } from "../content/blog"; import { getBlogs } from "../content/blog";
const posts = (await getBlogs()).sort( const posts = (await getBlogs()).sort(
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(), (a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
); );
const currentMonth = new Date().getMonth();
--- ---
<!doctype html> <!doctype html>
@@ -17,34 +18,20 @@ const posts = (await getBlogs()).sort(
<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} /> <BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
</head> </head>
<body <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> <main
<p class="font-bold text-2xl">kennethnym</p> class="py-8 max-w-2xl flex flex-col items-center space-y-0 leading-tight"
</header> >
<main class="py-8"> <p>KENNETHNYM v23.{currentMonth + 4}</p>
<p>dumping ground for my thoughts. all opinions are my own.</p> <p class="leading-none">&nbsp;</p>
<h1 class="font-bold mt-8 mb-2 text-lg visited">current projects:</h1> <p>software engineer. unpaid hhkb salesman.</p>
<ul class="not-prose space-y-4 md:space-y-2"> <p>&nbsp;</p>
<li> <p>&nbsp;</p>
<Link href="https://polygui.org">poly</Link>: a language-agnostic, <ul class="w-full">
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>
{ {
posts.map((post) => ( 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> <Link href={`/blog/${post.slug}`}>{post.data.title}</Link>
<FormattedDate date={post.data.pubDate} /> <FormattedDate date={post.data.pubDate} />
</li> </li>
@@ -52,7 +39,49 @@ const posts = (await getBlogs()).sort(
} }
</ul> </ul>
</main> </main>
<hr class="py-2" /> <div class="absolute w-full h-full px-2 -z-10 leading-tight">
<Footer /> <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> </body>
</html> </html>

View File

@@ -1,3 +1,43 @@
body { @font-face {
font-family: monospace, monospace; 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";
} }

View File

@@ -4,6 +4,7 @@ import catppuccin from "@catppuccin/tailwindcss";
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
export default { export default {
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"], content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
safelist: ["bg-peach", "text-red"],
theme: { theme: {
extend: {}, extend: {},
}, },