// Content lives in COMMANDS only: every entry renders in the terminal, and the // names double as the `help` listing. A `label -> url` line becomes a real link. // Adding a section to this site means adding one object here. const COMMANDS = [ { name: "hello", help: "the welcome message", body: `Hey :) I'm Momme. I build and break infrastructure, and I keep the pieces in Kubernetes so they can be put back together again. Type help to see what this box knows.`, }, { name: "whoami", help: "who is typing", body: `Momme Jürgensen — infrastructure and developer platforms. I like work that ends up as a short command instead of a long document. Currently at codeanker.`, }, { name: "projects", help: "projects — what I'm building", body: `nix-ha-kubernetes-cluster high-availability Kubernetes on NixOS, built to learn on brahmsee.digital event management platform for DLRG Schleswig-Holstein silvatica-web website of the Silvatica festival, Schwäbisch Hall momme.world this site — no build step, GitOps all the way down more of them at: https://github.com/supermomme`, detail: { "nix-ha-kubernetes-cluster": `An HA Kubernetes cluster on NixOS, built to learn rather than to impress. Control plane in HA, Ceph for storage, GitOps for everything above it. This site runs on it. -> https://github.com/supermomme/nix-ha-kubernetes-cluster`, "brahmsee.digital": `Event management platform for DLRG Schleswig-Holstein. Registrations and scheduling for a state water-rescue organisation, where the traffic arrives in waves of a few hundred people at once. -> https://brahmsee.digital`, "silvatica-web": `Website of the Silvatica festival in Schwäbisch Hall. -> https://github.com/supermomme/silvatica-web`, "momme.world": `This site. Plain files, a stock nginx image, three manifests, no build step. ArgoCD points at the repo, so a merged commit is a deployed site.`, }, }, { name: "stack", help: "the tools I reach for", body: `kubernetes self-hosted cluster, GitOps with ArgoCD, Terraform for the rest nix reproducible configs for servers and laptops observability Prometheus, Loki, Grafana — dashboards I actually read fpv drones built, flown, crashed, rebuilt`, }, { name: "social", help: "where I hang out online", body: `github -> https://github.com/supermomme mastodon -> https://cybre.town/@momme instagram -> https://instagram.com/supermommej`, }, { name: "contact", help: "reach me directly", body: `mail -> mailto:momme@juergensen.me matrix -> https://matrix.to/#/@momme:chaos.jetzt`, }, { name: "ls", help: "what's in here", body: () => names("projects").join(" "), }, { name: "cat", prefix: true, help: "cat README.md", run: (arg) => { const f = arg.toLowerCase(); if (f === "readme.md" || f === "readme") return body("hello"); if (f === ".ssh/id_rsa") return fail("nice try."); return fail(`cat: ${arg}: No such file or directory`, "try: cat README.md"); }, }, { name: "help", help: "this list", body: () => ["usage: ", "", ...usages()], run: (arg) => { if (!arg) return body("help"); const row = usages().find((line) => line.trim().startsWith(arg + " ")); return row ? row : fail(`no command called "${arg}"`, "see: help"); }, }, { name: "clear", help: "wipe the screen", body: "" }, { name: "date", help: "today's date", body: () => new Date().toDateString() }, { name: "time", help: "current time", body: () => new Date().toLocaleTimeString("de-DE") }, { name: "echo", prefix: true, help: "echo — repeat yourself", body: (arg) => arg }, { name: "pwd", help: "working directory", body: "/world" }, { name: "uname", prefix: true, help: "system information", body: "momme.world 1.0.0 static x86_64 GNU/freedom" }, { name: "curl", prefix: true, help: "curl momme.world — you already did that", body: "you are already reading the output :)" }, { name: "sudo", prefix: true, help: "elevate", body: "momme is not in the sudoers file. This incident has been reported." }, { name: "vim", prefix: true, help: "an editor", body: "emacs, obviously." }, { name: "emacs", prefix: true, help: "an OS with good editing", body: "a great OS, shame about the editor." }, { name: "coffee", help: "brew coffee", body: "() ~ I'm out of coffee." }, { name: "exit", help: "close the session", body: "close the tab, or just stop reading :)" }, ]; const ALIASES = { whois: "whoami", about: "whoami", openme: "whoami", tech: "stack", tools: "stack", links: "social", socials: "social", mail: "contact", email: "contact", repos: "projects", rm: "sudo", }; // ---------- content helpers ---------- const byName = (name) => COMMANDS.filter((c) => c.name === name && !c.prefix)[0]; const body = (name) => render(byName(name).body, ""); const render = (value, arg) => { const v = typeof value === "function" ? value(arg) : value; return Array.isArray(v) ? v.join("\n") : String(v); }; const names = (name) => split(body(name)).map((line) => line.trim().split(/\s{2,}/)[0]).filter(Boolean); const commandNames = () => unique().map((c) => c.name); const unique = () => COMMANDS.filter((c, i) => COMMANDS.findIndex((x) => x.name === c.name) === i); const usages = () => unique().map((c) => ` ${c.name.padEnd(9)} ${c.help}`); const fail = (message, ...more) => ({ error: message, more: more.filter(Boolean) }); const split = (value) => value.split("\n"); // ---------- dom ---------- const out = document.getElementById("out"); const screen = document.getElementById("screen"); const form = document.getElementById("prompt"); const input = document.getElementById("cmd"); const make = (tag, cls, value) => { const node = document.createElement(tag); if (cls) node.className = cls; if (value != null) node.textContent = value; return node; }; const scrollDown = () => (screen.scrollTop = screen.scrollHeight); function print(value, arg) { const resolved = typeof value === "function" ? value(arg) : value; if (resolved && resolved.error) { out.append(make("div", "line err", resolved.error)); resolved.more.forEach((line) => out.append(make("div", "line dim", line))); } else { split(render(resolved, arg)).forEach((line) => out.append(linked(line))); } scrollDown(); } // Turn `label -> url` (or a bare url) into text plus real anchors. const LINK = /(https?:\/\/\S+|mailto:\S+)/; function linked(line) { const node = make("span", "line out"); if (!LINK.test(line)) return (node.textContent = line), node; const arrow = line.indexOf("->"); if (arrow > 0) node.append(line.slice(0, arrow)); (arrow > 0 ? line.slice(arrow + 2) : line).split(LINK).forEach((part) => { if (!LINK.test(part)) return node.append(part); const anchor = make("a", null, part); anchor.href = part; if (part.startsWith("http")) anchor.rel = "noopener me"; node.append(anchor); }); return node; } // ---------- the loop ---------- const history = { items: [], at: 0 }; function recall(step) { history.at += step; if (history.at < 0) history.at = 0; if (history.at >= history.items.length) { history.at = history.items.length; input.value = ""; return; } input.value = history.items[history.at]; } function execute(raw) { const [word = "", ...rest] = raw.trim().split(/\s+/); const arg = rest.join(" "); const name = word.toLowerCase(); out.append(make("div", "line cmd", raw)); if (!name) return scrollDown(); if (name === "clear") return (out.textContent = ""); const wanted = ALIASES[name] || name; const found = COMMANDS.find((c) => c.name === wanted && !c.prefix) || COMMANDS.find((c) => c.name === wanted && c.prefix); if (!found) { const near = commandNames().filter((n) => n !== name && n[0] === name[0]).slice(0, 3); return print(fail(`command not found: ${name}`, near.length ? `did you mean: ${near.join(", ")}` : "see: help")); } if (found.name === "projects" && arg) { const key = arg.toLowerCase(); if (key === "list") return print(body("projects")); return print(found.detail[key] || fail(`no project called "${arg}"`, "names:", ...names("projects"))); } return print(found.run ? found.run(arg) : found.body, arg); } form.addEventListener("submit", (event) => { event.preventDefault(); const raw = input.value; input.value = ""; if (raw.trim()) history.items.push(raw.trim()); history.at = history.items.length; execute(raw); }); input.addEventListener("keydown", (event) => { if (event.key === "ArrowUp") { event.preventDefault(); recall(-1); } if (event.key === "ArrowDown") { event.preventDefault(); recall(1); } if (event.key !== "Tab") return; const word = input.value.trim().split(/\s+/)[0]; const hit = commandNames().filter((n) => word && n.startsWith(word)); if (hit.length === 1) { event.preventDefault(); input.value = hit[0] + " "; } }); screen.addEventListener("click", () => { if (!getSelection().toString()) input.focus(); }); document.querySelectorAll("[data-run]").forEach((button) => button.addEventListener("click", () => { execute(button.dataset.run); input.focus(); }) ); print(`hello — type help for the rest of it.\n`); input.focus();