aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-19 13:46:35 +0200
committeruser <user@clank>2026-08-19 13:46:35 +0200
commit8a68f0a20df574910c162d936cbd09b4bbb65daa (patch)
treec345630e5bf985bc37229edba0aa3f403176ac6c
parentslosh: the rename is formatting-shaped, walk blame past it (diff)
downloadslosh-8a68f0a20df574910c162d936cbd09b4bbb65daa.tar.gz
slosh-8a68f0a20df574910c162d936cbd09b4bbb65daa.tar.xz
slosh-8a68f0a20df574910c162d936cbd09b4bbb65daa.zip
slosh: a website that is a page, not a mock session
web_new/ is the front page slosh.foo serves: nav, install, what is different, docs list. The first draft drew itself as a fake slosh session -- tab-strip nav, box-art panes, a status-line footer -- and that chrome is gone: the page describes the thing instead of impersonating it. web_plan.md is the working notes. build-site assembles it with the rendered docs and the webdemo.
-rw-r--r--web_new/README.md45
-rwxr-xr-xweb_new/build-site41
-rw-r--r--web_new/index.html188
-rwxr-xr-xweb_new/install.sh56
-rw-r--r--web_new/site.css265
-rw-r--r--web_plan.md21
6 files changed, 616 insertions, 0 deletions
diff --git a/web_new/README.md b/web_new/README.md
new file mode 100644
index 0000000..b272b28
--- /dev/null
+++ b/web_new/README.md
@@ -0,0 +1,45 @@
+# web_new — the slosh website
+
+One front page, plus the two things the repository already builds, assembled
+into a single static site:
+
+| path | what | comes from |
+|---|---|---|
+| `/` | the front page: what it is, the demo video, install | `index.html` + `site.css`, hand-written, no build step |
+| `/docs/` | the documentation | `contrib/gen-docs`, the same pages `make docs` renders |
+| `/demo/` | the in-browser machine | `contrib/webdemo`, on its own page so the front page stays light |
+| `/install.sh` | what `curl \| sudo sh` fetches | `install.sh` here |
+
+## Build
+
+```sh
+web_new/build-site # everything -> build/www
+NO_DEMO=1 web_new/build-site # without the demo (it cross-compiles a guest)
+contrib/webdemo/serve --dir build/www
+```
+
+The output is only static files. Any host that serves a directory works, with
+the one condition the webdemo README already states: `.wasm` must be served as
+`application/wasm`.
+
+## Decisions
+
+- **The page is drawn like a session.** Tab strip as the nav, sections as
+ panes with the title set into the frame, a status line at the bottom. The
+ palette is `contrib/themes/sl0p.kdl`. This is what keeps it from looking
+ like every generated landing page: it looks like slosh, because it copies
+ slosh rather than a template.
+- **The demo lives on its own page.** Loading it costs ~11 MiB and a machine
+ boot; the front page costs two files and no JavaScript beyond the copy
+ buttons.
+- **No framework, no fonts fetched, no tracker.** Two files for the front
+ page. The system monospace stack everywhere the terminal is being imitated.
+
+## TODO
+
+- The demo video is a placeholder (`#video` in `index.html`; the comment there
+ says exactly what to replace). Drop in `demo.mp4` + `demo-poster.png` when
+ rendered.
+- `install.sh` clones `https://slosh.foo/src` (override: `SLOSH_REPO=`). Pin
+ the real public repo URL once it exists — same for the `src` links in
+ `index.html`.
diff --git a/web_new/build-site b/web_new/build-site
new file mode 100755
index 0000000..df1a20d
--- /dev/null
+++ b/web_new/build-site
@@ -0,0 +1,41 @@
+#!/bin/sh
+# Assemble the whole website into build/www:
+#
+# / web_new/ the front page (this directory)
+# /docs/ contrib/gen-docs the rendered documentation
+# /demo/ contrib/webdemo the in-browser machine, on its own page
+#
+# web_new/build-site everything
+# NO_DEMO=1 web_new/build-site skip the demo (it cross-compiles a
+# guest and needs zig + e2fsprogs)
+#
+# The output is only static files. Serve it with anything that serves a
+# directory; the one condition is .wasm as application/wasm, which
+# `contrib/webdemo/serve --dir build/www` handles locally.
+
+set -eu
+
+here="$(cd "$(dirname "$0")" && pwd)"
+root="$(dirname "$here")"
+out="$root/build/www"
+
+rm -rf "$out"
+mkdir -p "$out"
+
+# The front page.
+cp "$here/index.html" "$here/site.css" "$here/install.sh" "$out/"
+
+# The docs, rendered by the same generator `make docs` uses.
+python3 "$root/contrib/gen-docs" "$out/docs"
+
+# The demo, assembled by its own Makefile, on its own page.
+if [ "${NO_DEMO:-0}" = 1 ]; then
+ echo "build-site: skipping demo (NO_DEMO=1)"
+else
+ make -C "$root/contrib/webdemo" site
+ mkdir -p "$out/demo"
+ cp -R "$root/contrib/webdemo/build/site/." "$out/demo/"
+fi
+
+echo "build-site: $out"
+echo "build-site: serve with: contrib/webdemo/serve --dir build/www"
diff --git a/web_new/index.html b/web_new/index.html
new file mode 100644
index 0000000..e75b8ba
--- /dev/null
+++ b/web_new/index.html
@@ -0,0 +1,188 @@
+<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<title>slosh — a terminal multiplexer</title>
+<meta name="description" content="slosh: panes, tabs, sessions you can detach from. One static binary, written in C on libghostty-vt.">
+<link rel="stylesheet" href="site.css">
+</head>
+<body>
+
+<nav class="nav" aria-label="Site">
+ <a href="./" aria-current="page">slosh</a>
+ <a href="demo/">demo</a>
+ <a href="docs/">docs</a>
+ <span class="nav-fill"></span>
+ <a href="https://slosh.foo/src">src</a>
+</nav>
+
+<main>
+
+<header class="hero">
+ <h1>slosh</h1>
+ <p class="tagline">A terminal multiplexer — panes, tabs, sessions you can
+ detach from — written in C on top of
+ <a href="https://github.com/ghostty-org/ghostty">libghostty-vt</a>.</p>
+
+ <p class="hero-links">
+ <a class="btn" href="#install">install</a>
+ <a class="btn ghost" href="demo/">try it in the browser</a>
+ </p>
+</header>
+
+<!-- ================================================================ -->
+
+<section id="video">
+ <h2>watch it</h2>
+
+ <!-- PLACEHOLDER: replace this whole <div> with
+ <video controls poster="demo-poster.png" src="demo.mp4"></video>
+ once the demo capture is rendered. -->
+ <div class="video-placeholder">
+ <span class="play" aria-hidden="true">▶</span>
+ <span class="video-note">demo video — being rendered</span>
+ <span class="video-sub">meanwhile, the <a href="demo/">live demo</a> boots
+ a real Linux in your tab and drops you into a session</span>
+ </div>
+</section>
+
+<!-- ================================================================ -->
+
+<section id="install">
+ <h2>install</h2>
+
+ <div class="cmd" data-copy="curl -fsSL https://slosh.foo/install.sh | sudo sh">
+ <pre><code><span class="prompt">$</span> curl -fsSL https://slosh.foo/install.sh | sudo sh</code></pre>
+ <button class="copy" type="button" aria-label="Copy command">copy</button>
+ </div>
+
+ <p class="aside">Piping a URL into a root shell should make you flinch —
+ <a href="install.sh">read it first</a>, it is short. Or skip it entirely:
+ building from source needs <a href="https://ziglang.org">zig</a> 0.16 and
+ nothing else. No cmake, no autotools, no libraries. The result is one static
+ binary.</p>
+
+ <div class="cmd">
+ <pre><code><span class="prompt">$</span> git clone https://slosh.foo/src slosh &amp;&amp; cd slosh
+<span class="prompt">$</span> make vendor <span class="c"># the vendored terminal core, once</span>
+<span class="prompt">$</span> make <span class="c"># about a second</span>
+<span class="prompt">$</span> sudo install build/slosh /usr/local/bin/</code></pre>
+ </div>
+
+ <p>Then:</p>
+
+ <div class="cmd">
+ <pre><code><span class="prompt">$</span> slosh <span class="c"># attach to session "main", creating it if needed</span>
+<span class="prompt">$</span> slosh -s work <span class="c"># a named session</span>
+<span class="prompt">$</span> slosh ls <span class="c"># what is running</span></code></pre>
+ </div>
+
+ <p>Sessions survive detaching, and your ssh connection dying. Then
+ <kbd>C-a</kbd> <kbd>\</kbd> to split, <kbd>C-a</kbd> <kbd>?</kbd> for the
+ keys, <kbd>C-a</kbd> <kbd>p</kbd> for every command by name.</p>
+</section>
+
+<!-- ================================================================ -->
+
+<section id="different">
+ <h2>what is different about it</h2>
+
+ <dl class="points">
+ <dt>The mouse works properly.</dt>
+ <dd>Click the middle of a border to split toward it, drag a gap to move a
+ boundary, drag a pane by its title to swap it. Everything drawn registers
+ what it is as it is drawn, so a click and a preview cannot disagree.</dd>
+
+ <dt>The layout is recomputed, never stored.</dt>
+ <dd>Small terminal? The tab becomes a list of headers, and comes back to
+ exactly the arrangement you had. Nothing drifts, because there is nothing
+ stored to drift.</dd>
+
+ <dt>Dead panes keep their output.</dt>
+ <dd>A pane told to run something keeps what it printed when that thing
+ exits, with <code>[re-run]</code> and <code>[close]</code> in its
+ frame.</dd>
+
+ <dt>Configuration is one KDL file.</dt>
+ <dd>It reloads on save, can be built from <code>include</code>d pieces, and
+ refuses a broken file instead of half-applying it.</dd>
+
+ <dt>Colour passes over cells.</dt>
+ <dd>Dim, tint, rulers, spotlights, and a border that can flash when a pane
+ rings — with strengths written as expressions.</dd>
+
+ <dt>Scriptable to the same depth it is usable.</dt>
+ <dd>One JSON object per line over the session's socket, and a pane can draw
+ its own status bar and buttons with an escape sequence.</dd>
+ </dl>
+
+ <p class="aside">The multiplexers we have are crusty, and the things that
+ make them awkward follow from how the programs are built. So: start over, and
+ stand on the shoulders of the person who already solved the genuinely hard
+ part — the terminal core from <a href="https://ghostty.org">ghostty</a> does
+ the VT parsing, scrollback, selection, images and key encoding. What sits on
+ top is about 15k lines of C, and recomputes rather than remembers.
+ The twenty-one decisions behind it: <a href="docs/design.html">how it
+ works</a>.</p>
+</section>
+
+<!-- ================================================================ -->
+
+<section id="try">
+ <h2>try it without installing anything</h2>
+
+ <p>The <a href="demo/">demo page</a> boots a riscv64 Linux —
+ Fabrice Bellard's TinyEMU compiled to WebAssembly — with slosh running as the
+ thing you land in. Nothing is installed, nothing is uploaded, and reloading
+ the page throws the machine away. It streams about 11&nbsp;MiB once, then
+ never touches the network again.</p>
+
+ <p class="aside">It is an emulator interpreting every instruction, so the
+ machine is honest but not fast. The real thing redraws in microseconds.</p>
+</section>
+
+<!-- ================================================================ -->
+
+<section id="docs">
+ <h2>docs</h2>
+
+ <ul class="doclist">
+ <li><a href="docs/keys.html">keys</a><span> — the leader, the defaults, rebinding, the palette</span></li>
+ <li><a href="docs/panes.html">panes and tabs</a><span> — splitting, moving, zooming, the mouse</span></li>
+ <li><a href="docs/config.html">configuration</a><span> — one file, live reload, themes, include</span></li>
+ <li><a href="docs/shaders.html">shaders</a><span> — colour passes over a pane's contents</span></li>
+ <li><a href="docs/chrome.html">chrome</a><span> — the same passes over a pane's frame</span></li>
+ <li><a href="docs/layouts.html">layouts</a><span> — a session as a file you check in</span></li>
+ <li><a href="docs/scripting.html">scripting</a><span> — the control socket, and what a pane can draw</span></li>
+ <li><a href="docs/design.html">how it works</a><span> — the two decisions that shape the rest</span></li>
+ </ul>
+</section>
+
+</main>
+
+<footer class="foot">
+ <span>MIT</span>
+ <span>C on <a href="https://github.com/ghostty-org/ghostty">libghostty-vt</a></span>
+ <span>one static binary</span>
+</footer>
+
+<script>
+// The one script on the page: the copy buttons. No framework, no tracker,
+// nothing fetched from anywhere.
+for (const box of document.querySelectorAll(".cmd[data-copy]")) {
+ const btn = box.querySelector(".copy");
+ btn.addEventListener("click", async () => {
+ try {
+ await navigator.clipboard.writeText(box.dataset.copy);
+ btn.textContent = "copied";
+ setTimeout(() => (btn.textContent = "copy"), 1200);
+ } catch {
+ btn.textContent = "select it";
+ }
+ });
+}
+</script>
+
+</body>
+</html>
diff --git a/web_new/install.sh b/web_new/install.sh
new file mode 100755
index 0000000..f2b8c97
--- /dev/null
+++ b/web_new/install.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+# slosh installer -- https://slosh.foo
+#
+# You are reading this before running it, which is correct. What it does:
+#
+# 1. checks for zig 0.16 and git,
+# 2. clones the source into a temporary directory,
+# 3. `make vendor && make` -- the vendored terminal core, then one static
+# binary, no other dependencies,
+# 4. installs build/slosh into /usr/local/bin (the sudo is for this step
+# and nothing else).
+#
+# Nothing is downloaded except the git clone and what `make vendor` fetches
+# (the pinned libghostty-vt). No package manager, no config written, nothing
+# started. Uninstall is `rm /usr/local/bin/slosh`.
+
+set -eu
+
+REPO="${SLOSH_REPO:-https://slosh.foo/src}" # TODO: pin to the public repo URL
+PREFIX="${PREFIX:-/usr/local}"
+ZIG="${ZIG:-zig}"
+
+say() { printf ' %s\n' "$*"; }
+fail() { printf 'install.sh: %s\n' "$*" >&2; exit 1; }
+
+command -v git >/dev/null 2>&1 || fail "git is required"
+
+# zig 0.16, the one thing the build wants. Also try the location the
+# Makefile's own docs suggest.
+if ! command -v "$ZIG" >/dev/null 2>&1; then
+ if [ -x "$HOME/zig-0.16.0/zig" ]; then
+ ZIG="$HOME/zig-0.16.0/zig"
+ else
+ fail "zig 0.16 not found -- https://ziglang.org/download, or ZIG=/path/to/zig"
+ fi
+fi
+case "$("$ZIG" version 2>/dev/null)" in
+ 0.16.*) ;;
+ *) fail "zig 0.16 is required, found $("$ZIG" version 2>/dev/null || echo nothing)" ;;
+esac
+
+tmp="$(mktemp -d)"
+trap 'rm -rf "$tmp"' EXIT INT TERM
+
+say "cloning $REPO"
+git clone --quiet --depth 1 "$REPO" "$tmp/slosh"
+
+say "building (make vendor, once; then make)"
+make -C "$tmp/slosh" ZIG="$ZIG" vendor
+make -C "$tmp/slosh" ZIG="$ZIG"
+
+say "installing to $PREFIX/bin/slosh"
+install -d "$PREFIX/bin"
+install -m 755 "$tmp/slosh/build/slosh" "$PREFIX/bin/slosh"
+
+say "done. run: slosh"
diff --git a/web_new/site.css b/web_new/site.css
new file mode 100644
index 0000000..a46e45f
--- /dev/null
+++ b/web_new/site.css
@@ -0,0 +1,265 @@
+/* slosh.foo — one page, one stylesheet, no build step.
+ *
+ * The palette is contrib/themes/sl0p.kdl — hot pink on near-black — so the
+ * page and the thing it describes agree. Deliberately not a landing page:
+ * no gradients, no cards, no icons, nothing floats in on scroll. */
+
+:root {
+ --bg: #0b0b0e;
+ --panel: #141418;
+ --code-bg: #1c1c22;
+ --fg: #e8e8ea;
+ --dim: #8a8a95;
+ --faint: #45454a;
+ --pink: #ff5fd7;
+ --dead: #ff875f;
+ --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas,
+ "DejaVu Sans Mono", monospace;
+ --sans: system-ui, -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
+}
+
+* { box-sizing: border-box; }
+
+html { scroll-behavior: smooth; }
+
+body {
+ margin: 0;
+ background: var(--bg);
+ color: var(--fg);
+ font: 16px/1.65 var(--sans);
+ -webkit-font-smoothing: antialiased;
+}
+
+a { color: var(--pink); text-decoration: none; }
+a:hover { text-decoration: underline; }
+
+code, kbd, pre { font-family: var(--mono); }
+code { font-size: 0.9em; }
+
+kbd {
+ padding: 0.05em 0.35em;
+ border: 1px solid var(--faint);
+ border-radius: 3px;
+ background: var(--panel);
+ color: var(--pink);
+ font-size: 0.85em;
+}
+
+/* ---- the nav -------------------------------------------------------- */
+
+.nav {
+ position: sticky;
+ top: 0;
+ z-index: 3;
+ display: flex;
+ gap: 1.5em;
+ overflow-x: auto;
+ padding: 0 1.25rem;
+ border-bottom: 1px solid var(--panel);
+ background: var(--bg);
+ font-family: var(--mono);
+ font-size: 13px;
+ line-height: 2.6;
+ white-space: nowrap;
+}
+
+.nav a { color: var(--dim); }
+.nav a:hover { color: var(--pink); text-decoration: none; }
+.nav a[aria-current] { color: var(--fg); }
+.nav-fill { flex: 1; }
+
+/* ---- the hero ------------------------------------------------------- */
+
+main {
+ max-width: 60rem;
+ margin: 0 auto;
+ padding: 0 1.25rem 4rem;
+}
+
+.hero { padding: 3.5rem 0 1rem; }
+
+.hero h1 {
+ margin: 0;
+ color: var(--pink);
+ font-family: var(--mono);
+ font-size: clamp(2.2rem, 6vw, 3.4rem);
+ font-weight: 600;
+ letter-spacing: 0.04em;
+}
+
+.tagline {
+ margin: 0.5rem 0 0;
+ max-width: 38rem;
+ color: var(--dim);
+ font-size: 1.05rem;
+}
+
+.hero-links { margin: 2rem 0 0; }
+
+.btn {
+ display: inline-block;
+ margin-right: 0.75rem;
+ padding: 0.35rem 1.1rem;
+ background: var(--pink);
+ color: var(--panel);
+ font-family: var(--mono);
+ font-size: 14px;
+}
+.btn:hover { background: #fff; text-decoration: none; }
+
+.btn.ghost {
+ background: transparent;
+ border: 1px solid var(--faint);
+ color: var(--dim);
+}
+.btn.ghost:hover { border-color: var(--pink); color: var(--pink); background: transparent; }
+
+/* ---- sections ------------------------------------------------------- */
+
+section { margin: 3.5rem 0 0; }
+
+section h2 {
+ margin: 0 0 1rem;
+ padding-bottom: 0.4rem;
+ border-bottom: 1px solid var(--panel);
+ color: var(--fg);
+ font-family: var(--mono);
+ font-size: 1.1rem;
+ font-weight: 600;
+}
+
+section p { max-width: 44rem; }
+section > p:last-child { margin-bottom: 0; }
+
+.aside { color: var(--dim); font-size: 0.95rem; }
+
+/* ---- the video placeholder ------------------------------------------ */
+
+.video-placeholder {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+ aspect-ratio: 16 / 9;
+ border: 1px dashed var(--faint);
+ border-radius: 4px;
+ background:
+ repeating-linear-gradient(-45deg,
+ transparent 0 14px, rgba(255, 255, 255, 0.015) 14px 28px),
+ var(--panel);
+ text-align: center;
+}
+
+.video-placeholder .play {
+ color: var(--faint);
+ font-size: 2.5rem;
+ line-height: 1;
+}
+
+.video-note {
+ color: var(--dim);
+ font-family: var(--mono);
+ font-size: 14px;
+}
+
+.video-sub { max-width: 28rem; padding: 0 1rem; color: var(--faint); font-size: 13px; }
+
+#video video { /* the eventual replacement inherits the frame */
+ width: 100%;
+ border-radius: 4px;
+}
+
+/* ---- command blocks ------------------------------------------------- */
+
+.cmd {
+ position: relative;
+ margin: 1rem 0;
+ border: 1px solid var(--code-bg);
+ border-radius: 4px;
+ background: var(--code-bg);
+}
+
+.cmd pre {
+ margin: 0;
+ padding: 0.8rem 1rem;
+ overflow-x: auto;
+ font-size: 13.5px;
+ line-height: 1.7;
+}
+
+.cmd .prompt { color: var(--faint); user-select: none; }
+.cmd .c { color: var(--dim); }
+
+.copy {
+ position: absolute;
+ top: 0.45rem;
+ right: 0.45rem;
+ padding: 0.1rem 0.6rem;
+ border: 1px solid var(--faint);
+ border-radius: 3px;
+ background: var(--panel);
+ color: var(--dim);
+ font-family: var(--mono);
+ font-size: 12px;
+ cursor: pointer;
+}
+.copy:hover { border-color: var(--pink); color: var(--pink); }
+
+/* ---- the points list ------------------------------------------------ */
+
+.points { margin: 0.5rem 0 1.25rem; }
+
+.points dt {
+ margin-top: 1.1rem;
+ color: var(--fg);
+ font-weight: 600;
+}
+.points dt:first-child { margin-top: 0; }
+
+.points dd {
+ margin: 0.15rem 0 0;
+ max-width: 44rem;
+ color: var(--dim);
+}
+
+/* ---- the docs list -------------------------------------------------- */
+
+.doclist {
+ margin: 0.5rem 0 0;
+ padding: 0;
+ list-style: none;
+ columns: 2;
+ column-gap: 2.5rem;
+}
+
+.doclist li {
+ margin: 0.35rem 0;
+ break-inside: avoid;
+ font-size: 0.95rem;
+}
+
+.doclist a { font-family: var(--mono); font-size: 14px; }
+.doclist span { color: var(--faint); }
+
+/* ---- the footer ----------------------------------------------------- */
+
+.foot {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0 1.5rem;
+ padding: 0.4rem 1.25rem;
+ border-top: 1px solid var(--panel);
+ color: var(--faint);
+ font-family: var(--mono);
+ font-size: 12.5px;
+ line-height: 2;
+}
+
+.foot a { color: var(--dim); }
+
+/* ---- small screens -------------------------------------------------- */
+
+@media (max-width: 44rem) {
+ .doclist { columns: 1; }
+}
diff --git a/web_plan.md b/web_plan.md
new file mode 100644
index 0000000..677c266
--- /dev/null
+++ b/web_plan.md
@@ -0,0 +1,21 @@
+ok listen up dude,
+
+we're going to need a good webpage for this slosh thing
+
+we have some semi good (generated) docs/ website thing already
+
+also we have very cool web-based linux demo system in ./contrib/webdemo/
+
+we should integrate all of these to a good single page website (minus the live demo thing maybe can live on its own page to reduce innitial clutter0
+
+page will be simple in terms of getting the point accross what slosh is/does
+
+we'll render a good demo video (TBD, put placeholder)
+
+some `curl http://slosh.foo/install.sh | sudo sh` instructions
+
+make sure it doesn't look like 1 of those thousand a dozen claude-opus webpages
+
+super tired of those
+
+lets go, u put artifacts in ./web_new (mkdir, not exist yet)