WIP: vim emulation
This commit is contained in:
BIN
public/fonts/CommitMono-400-Italic.otf
Normal file
BIN
public/fonts/CommitMono-400-Italic.otf
Normal file
Binary file not shown.
BIN
public/fonts/CommitMono-400-Regular.otf
Normal file
BIN
public/fonts/CommitMono-400-Regular.otf
Normal file
Binary file not shown.
BIN
public/fonts/CommitMono-700-Italic.otf
Normal file
BIN
public/fonts/CommitMono-700-Italic.otf
Normal file
Binary file not shown.
BIN
public/fonts/CommitMono-700-Regular.otf
Normal file
BIN
public/fonts/CommitMono-700-Regular.otf
Normal file
Binary file not shown.
BIN
public/fonts/SymbolsNerdFontMono-Regular.ttf
Normal file
BIN
public/fonts/SymbolsNerdFontMono-Regular.ttf
Normal file
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 483 KiB |
117
src/components/CommandLine.astro
Normal file
117
src/components/CommandLine.astro
Normal 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"> </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>
|
44
src/components/LuaLine.astro
Normal file
44
src/components/LuaLine.astro
Normal 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"
|
||||||
|
> NORMAL </span
|
||||||
|
>
|
||||||
|
<div class="flex flex-row bg-surface0">
|
||||||
|
<span> </span>
|
||||||
|
<Link href="https://github.com/kennethnym">github</Link>
|
||||||
|
<span> </span>
|
||||||
|
<Link href="https://x.com/kennethnym">x.com</Link>
|
||||||
|
<span> </span>
|
||||||
|
<Link href="mailto:kennethnym@outlook.com">email</Link>
|
||||||
|
<span> </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 = " COMMAND ";
|
||||||
|
statusIndicator.classList.remove("bg-blue");
|
||||||
|
statusIndicator.classList.add("bg-peach");
|
||||||
|
console.log("cmd mode!");
|
||||||
|
});
|
||||||
|
|
||||||
|
cmdLine.addEventListener("cmdmodedisabled", () => {
|
||||||
|
statusIndicator.innerHTML = " NORMAL ";
|
||||||
|
statusIndicator.classList.add("bg-blue");
|
||||||
|
statusIndicator.classList.remove("bg-peach");
|
||||||
|
});
|
||||||
|
</script>
|
@@ -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>
|
||||||
|
@@ -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"> </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> </p>
|
||||||
<li>
|
<p> </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>
|
||||||
|
@@ -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";
|
||||||
}
|
}
|
||||||
|
@@ -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: {},
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user