The page you are reading is the case. Every page on the site is one terminal
window: the front page is whoami and ls ~/projects/, the cases are README
files, and the shell is a prompt that answers back.
It is also the one project here where all of the code can be talked about, so this page is mostly about how it is built.
No framework, and still a document per page
There is no React, no Vue and no router library. The views are plain functions
that turn data into a string of markup, with no DOM and no browser globals in
them. That restriction is the whole trick: because they never touch document,
the same functions run in Node at build time.
After vite build, a prerender step writes one index.html per route with the
page already rendered into it, its own title and description, a canonical URL,
Open Graph tags and JSON-LD. A crawler, a link preview or a reader with a slow
connection gets a finished document instead of an empty div waiting for a
bundle. The Open Graph images are drawn at the same moment, as the same terminal
window, so a shared link unfurls into the page it points at.
A case is fetched, not bundled
Case prose is the largest thing on the site, and most visitors never open one. So it is not in the JavaScript. The markdown plugin keeps the rendered body in an export the client never imports, and opening a case fetches the prerendered page, lifts the window out of it and swaps it in. Hovering a card starts that fetch early, and a failed fetch simply hands the browser the link.
The swap runs through the View Transitions API. A project card and the window it opens share a transition name, so the card grows into the window and shrinks back when it closes, while the title bar holds still above it.
The shell is a shell
The prompt at /shell does more than it has to, on purpose:
- Line editing and history. The caret moves, ArrowUp walks the history and ctrl+r searches it. History survives a reload, and so does the scrollback.
- Tab completion. Command names on the first word, paths after
cd,catandls, with the candidates listed when there is more than one. - A filesystem.
ls ~/projectsis generated from the same case data the front page renders, so the two cannot disagree. - Operators.
&&,||and;chain commands,|is a real pipe that hands one command's output to the next as input, and&sends a job to the background and reports back when it is done. - Programs that take the window.
vim,htopand the rest take over the whole window the way a terminal's alternate screen does, and put everything back when they close.
Each command is one class that declares its own name, help line, manual page
and completion. help, man and Tab all read from the same list, so a new
command is a file and a line.
Everything moves, and nothing is downloaded for it
The background is a canvas: a honeycomb of dots that tetris, snake and
pingpong are played on. The rules of those games live in modules with no DOM
and no timing, which is what lets the build check them. Every sound is
synthesised from oscillators and a noise buffer, so there are no audio files.
Colours come from CSS variables, which is why switching the theme carries over
to the canvas as well. Under reduced motion the grid is drawn once and the
transitions are skipped.
Built every night
GitHub Actions builds the site on every push and once a night, then copies it to
the host. The nightly build is what keeps the numbers on the about page and the
open source metadata from GitHub current without anyone touching them. Before
that, npm run check asserts the routing, the link tagging, the filesystem
paths and the game rules.
Start with help in the shell. Try not to start with sudo rm -rf /.