<feed xmlns='http://www.w3.org/2005/Atom'>
<title>slosh.git/web, branch master</title>
<subtitle>an opinionated terminal multiplexer in C with programmable pane chrome</subtitle>
<id>https://git.sl0p.foo/slosh.git/atom/web?h=master</id>
<link rel='self' href='https://git.sl0p.foo/slosh.git/atom/web?h=master'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/'/>
<updated>2026-09-24T10:53:15Z</updated>
<entry>
<title>web: plainer front page copy -- themes, effects, scripting, floats, install</title>
<updated>2026-09-24T10:53:15Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-24T10:53:15Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=3f470e18c7e93d5404e1b86ba5a39995400214b9'/>
<id>urn:sha1:3f470e18c7e93d5404e1b86ba5a39995400214b9</id>
<content type='text'>
</content>
</entry>
<entry>
<title>webdemo: the picker in the machine, and a guest that proves its themes</title>
<updated>2026-09-22T14:00:10Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-22T14:00:10Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=b8e931b548e5e71fd001b9b5e9bbfce8574c7bb6'/>
<id>urn:sha1:b8e931b548e5e71fd001b9b5e9bbfce8574c7bb6</id>
<content type='text'>
The demo image was cut before any of this existed: an old guest binary with
no `C-a t` in it, and a config whose theme was a pasted palette. Rebuilt --
guest binary, root filesystem, site -- so the thing a visitor lands in is the
thing the front page is now claiming.

The overlay already kept every shipped theme in ~/.config/slosh/themes (they
are symlinks into contrib/, copied by content because a symlink in an ext2
image would dangle), which turns out to be exactly the directory the picker
looks in first -- and in the guest it is the *only* one, since /usr/bin/slosh
has no share/slosh beside it. So `boot-check` now asks the guest itself:
`{"cmd":"theme"}` over --script, expecting the worn theme and the shipped list
back. It failed honestly the first time, for the right reason (no HOME in a
phase that boots straight to /bin/sh, so no config, so no themes/ beside it),
which is the kind of failure a check is for.

The welcome float grew a fourth line -- `C-a t`, every theme, worn as you
browse -- and a row to hold it; the motd and the config's own comments say the
same. A visitor who reads three lines and presses one key should land on the
feature the page just sold them.
</content>
</entry>
<entry>
<title>web: a recording of the theme picker, and the tour step for it</title>
<updated>2026-09-22T13:52:17Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-22T13:52:17Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=0c51991c12be65856d2e31685b607b12ecd0f4fa'/>
<id>urn:sha1:0c51991c12be65856d2e31685b607b12ecd0f4fa</id>
<content type='text'>
The landing page's feature tour is scripted recordings -- driver scripts
against `slosh --script` on a virtual clock, re-rendered by `make` so a UI
change can never leave a stale demo -- and the picker is exactly the kind of
thing that does not survive being described. "Moving the selection applies the
theme" is a sentence; twenty-six seconds of a session changing colour under a
list is the claim itself.

demos/themes.py: a sidebar session with two tabs and a build pane that says it
is busy (so the spinner is in shot, turning in whatever colour the theme being
tried says it should), `cat config.kdl` showing a config that names a theme and
holds no colours at all, then C-a t -- the list, two themes worn rather than
swatched, `pho` typed to narrow, C-s writing the one line, a second `cat`
proving it wrote that line and nothing else, and finally a browse that ends in
Escape, which puts back what was worn.

Two rules the demo had to obey, both now in PLAN.md beside the other lessons:
only dark themes, because cells slosh does not paint keep the terminal's own
background and the cast header fixes that once -- `paper` would look broken in
the player and fine in a light terminal; and nothing on screen may carry this
machine, so the themes are copied next to the temp config rather than pointed
at with `theme_dir`, leaving a `cat` frame that reads as a name and a
directory nobody has to have.

The step slots after "make it yours, live", which is where a reader has just
been shown a config being edited by hand and is best placed to notice that a
theme is one word of it. Casts are built at publish time, so build-site picks
the new one up with no wiring.
</content>
</entry>
<entry>
<title>theme: a name and a directory, not sixty colours in your config</title>
<updated>2026-09-22T10:42:15Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-22T10:42:15Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=faa3fb6ee575a900abc574b0f9ed5ff291761837'/>
<id>urn:sha1:faa3fb6ee575a900abc574b0f9ed5ff291761837</id>
<content type='text'>
Wearing a theme meant pasting its `theme { }` block into config.kdl, or
including a path to a file you had copied there yourself. Both make the
palette a literal in your config, and a literal is the one thing you cannot
change cheaply: not from a script, not from a running session, not by a list
of what is available, and never again from upstream -- the colours you pasted
are the colours that theme had the day you pasted them.

So a theme is a file in a directory now, and a config names it:

    theme_name "phosphor"
    theme { frame_focus "#00ff88" }   // ...but that one colour is mine

theme_name is applied after the includes and before everything the file says
itself, which is the rule includes already follow and for the same reason: the
loader reads a document by asking it for names rather than walking it in order,
so "here" is not a position it could honour, and "what you asked for is the
base, what you wrote is yours" is the rule worth having.

Two directories, yours first: `themes/` beside the root config -- beside the
file that was loaded, so a config somewhere else (SLOSH_CONFIG, or one handed
to --check) takes its themes with it -- and then &lt;prefix&gt;/share/slosh/themes,
compiled in from PREFIX. install(1) has been putting themes there all along
and nothing could read them; now a shipped theme is nameable without being
copied, and a file of yours with the same name shadows it, which is how a
root-owned theme gets edited.

A name, not a path. The directory is what makes a theme switchable, listable
and shadowable, and a path silently does none of those, so `theme_name
"../x.kdl"` is refused with a line pointing at `include`, which still works
and is still the answer for a file somewhere a directory of names cannot
reach. A theme that is not there is one complaint and no more (D9): the rest
of the config applies, because losing your keybindings over a mistyped colour
scheme would be the wrong trade. A theme file is read by the same loader, so
it is watched like any other: saving one reloads the session wearing it, and
the derivations (tab_status_fg, tab_idle_bg) run over the theme's own colours
exactly as before.

Switching without editing anything, over the socket a session already has:

    slosh -s main cmd theme                                # amber default* ...
    slosh -s main cmd '{"cmd":"theme","name":"amber"}'      # now
    slosh -s main cmd '{"cmd":"theme","name":"amber","save":true}'

The session holds the name over later reloads, so saving your config for an
unrelated reason does not put the old colours back; `save` writes the
theme_name line into the file, replacing the one already there and leaving
every other line and comment alone -- dumping the whole config back over
itself would reformat a hand-written file and drop its comments, which is not
a fair price for changing a colour scheme. A name is tried on a throwaway
config first, so a typo costs nothing. An empty name gives the answer back to
the file.

--dump-config stops writing colours: it writes the theme's name and the
colours a config overrode by hand, so a dumped config keeps following its
theme instead of freezing it. --dump-theme is the other half and is new: the
resolved palette as a theme file, which is how you start one from the session
in front of you rather than from an empty file. It is also what keeps
contrib/themes/default.kdl honest -- that file is now the annotated reference
(the commented palette moved there out of config.kdl, where it was 119 lines
of colour in the middle of the prose), and a test compares it against the
compiled-in one.

While in there: tests/test_themes.py's "every theme sets every colour" guard
had been grepping for `&amp;c-&gt;`, the spelling before the colour table moved to
offsetof, so it had been finding zero colours and passing for months. Fixed,
with the derived and opt-in colours exempted for the reasons they are derived
and opt-in, and with a check that the guard found a table at all.
</content>
</entry>
<entry>
<title>web: boot the demo before publishing it</title>
<updated>2026-09-12T14:06:02Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-12T14:06:02Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=3887858082dfcdc23a368a69f0ee7c6eb74a1759'/>
<id>urn:sha1:3887858082dfcdc23a368a69f0ee7c6eb74a1759</id>
<content type='text'>
v0.1.7 shipped a demo that could not start a session, and the site was
live and broken because nothing between `make` and `rsync` ever ran the
guest. The demo is the one artifact here whose build succeeding says
nothing about it working: it is a cross-compiled riscv64 binary in an
ext2 image, so the failures the rest of build-site would notice -- a
compiler error, a missing file -- are not where its failures live. A
guest that segfaults on startup links, packages and copies perfectly,
and the page then loads, draws a terminal and sits there dead.

web/demo/boot-check already boots that exact image in a native emulator
and fails if init does not reach a composed screen. It took twenty
seconds to find what the browser reported as one unexplained line, so
it runs in the build now, between `make site` and the copy into the
staging tree. NO_BOOT_CHECK=1 opts out; NO_DEMO=1 skips it with the
rest of the demo.
</content>
</entry>
<entry>
<title>sidebar: statuses wear italic instead of an indent, and can wear colour</title>
<updated>2026-09-10T14:32:10Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-10T14:32:10Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=a2d08f4876226446c0052f033e8afea2f2ffbbf1'/>
<id>urn:sha1:a2d08f4876226446c0052f033e8afea2f2ffbbf1</id>
<content type='text'>
Three columns of indent is a fifth of a sixteen-column sidebar, spent
saying 'this belongs to the row above'. The slant says it in none: a
status now starts in the same column its tab's name starts in, and is
set in italic. That also survives the monochrome themes in contrib,
where a colour-only distinction would have had nothing to spend.

tab_status_fg / tab_status_bg are there for themes that do have colour
to spend. The fg follows tab_count when a theme does not name it -- the
same declared/derived split apply_scrolled makes -- so the six themes
in contrib get coherent status rows without a line added to any of
them. The bg is unset by default, because a band is opt-in and 'none'
is what a translucent terminal wants; the row is padded so a band that
*is* set covers the row rather than the words on it.

Two things fell out. The padding loop existed three times and is now
pad_cells. And cb_color wrote #000000 for an unset colour, so
 quietly pinned attach_bg -- 'gray
ink on the terminal's own background', says its comment -- to a black
band. Unset colours are now written commented out, the same trick the
states block uses for what it wants re-derived rather than pinned.
</content>
</entry>
<entry>
<title>build: a vendor bump rebuilds libghostty-vt, everywhere it is consumed</title>
<updated>2026-09-10T09:19:54Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-10T09:19:54Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=16620e7fd9a6780756dc956466c3579e5bb2f384'/>
<id>urn:sha1:16620e7fd9a6780756dc956466c3579e5bb2f384</id>
<content type='text'>
Three stale-build landmines, all found by shipping v0.1.7 -- the first
release where new code (GhosttySearch) actually needed the vendor bump
of 2026-09-01:

- $(VT_LIB) had no prerequisites, so an existing .a *was* the build:
  the macOS builder compiled pane.c against zig-out headers from before
  the bump. The vendor manifest is now the prerequisite; a re-vendor
  rewrites it, so a bump rebuilds the lib and its installed headers.

- web/demo had the same guard and also never passed -Dversion-string:
  upstream's build.zig ran git describe in our repo, and on the tag
  checkout web/publish builds from, a slosh tag is a fatal version
  mismatch. Both fixed; both had hidden behind a stale build/vt.

- The builder's zig-pkg (excluded from the sync, and left half-empty by
  a once-interrupted push) is an input, not a cache: it is now synced
  separately and additively, so a bump's new packages arrive and a
  partial local tree cannot maim the builder's copy.
</content>
</entry>
<entry>
<title>web: on one column the hero hovers, not a copy of the logo</title>
<updated>2026-09-03T17:19:58Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-03T17:19:58Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=8dab8bd4abf96dd1b05267e0ca853fa07bd377a2'/>
<id>urn:sha1:8dab8bd4abf96dd1b05267e0ca853fa07bd377a2</id>
<content type='text'>
The stage-resident logo copy goes: instead the top section itself --
logo and buttons -- is position: sticky below 54rem, full-bleed, with
the tour's stage tucked under it at --hero-h, which the page script
measures (wrapping buttons make it no fixed number; a ResizeObserver
keeps it honest). The tagline yields the room: with the header on
screen at all times, every line it keeps is a line the tour loses.
Desktop is exactly as before -- the floating logo, its observers and
its collision test untouched.
</content>
</entry>
<entry>
<title>web: the floating logo, on one-column screens too</title>
<updated>2026-09-03T17:16:01Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-03T17:16:01Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=02246aa9542340d5ea62121899432b7ad4273e5e'/>
<id>urn:sha1:02246aa9542340d5ea62121899432b7ad4273e5e</id>
<content type='text'>
Below 54rem the fixed corner the float centres itself in is content, not
air, so the float stays hidden there -- but the logo can ride where the
terminal does: a small copy inside the sticky stage, above the player, on
screen at all times. No observer, no collision test; it sticks with the
stage. Same link back to the top, same wash and shine (the shared rules
now name both), same reduced-motion fallback.
</content>
</entry>
<entry>
<title>web/recordings: a move cursor while the pointer drags</title>
<updated>2026-09-03T17:10:42Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-03T17:10:42Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=c7c1a8a8973ab6596d9eecafc9411fbfd519a803'/>
<id>urn:sha1:c7c1a8a8973ab6596d9eecafc9411fbfd519a803</id>
<content type='text'>
The cast already marks the button in the ptr track ([x, y, 1]); the player
now remembers the cell where it went down and, the moment the pointer
leaves it, swaps the arrow for a four-way move cursor (a clip-path, like
the arrow) centred on the old tip. A still click never triggers it, so
the border-click split keeps its arrow while the gap drag and both
pane-carries in the panes demo read as carrying. Player-side only: every
existing cast gets it without re-rendering.
</content>
</entry>
</feed>
