diff options
| author | user <user@clank> | 2026-08-19 13:46:35 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-19 13:46:35 +0200 |
| commit | 8a68f0a20df574910c162d936cbd09b4bbb65daa (patch) | |
| tree | c345630e5bf985bc37229edba0aa3f403176ac6c | |
| parent | slosh: the rename is formatting-shaped, walk blame past it (diff) | |
| download | slosh-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.md | 45 | ||||
| -rwxr-xr-x | web_new/build-site | 41 | ||||
| -rw-r--r-- | web_new/index.html | 188 | ||||
| -rwxr-xr-x | web_new/install.sh | 56 | ||||
| -rw-r--r-- | web_new/site.css | 265 | ||||
| -rw-r--r-- | web_plan.md | 21 |
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 && 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 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) |
