<feed xmlns='http://www.w3.org/2005/Atom'>
<title>slosh.git, branch v0.1.8</title>
<subtitle>an opinionated terminal multiplexer in C with programmable pane chrome</subtitle>
<id>https://git.sl0p.foo/slosh.git/atom/?h=v0.1.8</id>
<link rel='self' href='https://git.sl0p.foo/slosh.git/atom/?h=v0.1.8'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/'/>
<updated>2026-09-22T14:13:07Z</updated>
<entry>
<title>release: v0.1.8</title>
<updated>2026-09-22T14:13:07Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-22T14:13:07Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=4c3952aa72c1fdf52a29bc53804316c711da55d3'/>
<id>urn:sha1:4c3952aa72c1fdf52a29bc53804316c711da55d3</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: the busy spinner is its own colour (tab_status_spinner)</title>
<updated>2026-09-22T13:38:08Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-22T13:38:08Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=1cbfb4474ca31c61254be8d5f3ec046967bd1ff3'/>
<id>urn:sha1:1cbfb4474ca31c61254be8d5f3ec046967bd1ff3</id>
<content type='text'>
It was drawn in tab_status_busy, the colour of the words it sits beside, which
made one decision out of two. The words have to stay readable at a glance and
a mark does not: a theme may well want the glyph loud and the text calm -- or
the text in the accent and the mark a quiet dot -- and with one entry neither
is available without moving both.

Unset it follows tab_status_busy, derived after it and from whatever that
resolved to, named or derived, so nothing changes for anyone who does not ask
and no theme in contrib needs a line. `--dump-theme` writes both, like every
other colour the compositor draws.
</content>
</entry>
<entry>
<title>sidebar: room for the spinner, by default (tab_bar_padding 0 1 0 2)</title>
<updated>2026-09-22T13:24:16Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-22T13:24:16Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=ecdc8d0c335c35a74b46ddb22773e8ef4b222891'/>
<id>urn:sha1:ecdc8d0c335c35a74b46ddb22773e8ef4b222891</id>
<content type='text'>
`│⠋bash: cd` -- the mark landed in the one column of left padding there was,
so it ended up welded to the first letter of the status. The rule was "the
mark takes a column the padding was already holding blank", which is right
about not reflowing and wrong about there being enough of it.

So the mark takes the *first* column of the indent and whatever is left over
is the air after it, and the default indent is two columns instead of one. One
column is still available -- `tab_bar_padding 0 1` -- and now means the
squashed look on purpose rather than by default; three gives two spaces; zero
still puts everything against the frame. Nothing reflows when a program starts
or stops working, which was the point of taking a column that was already
blank.

The labels and the tab numbers move with it, because the indent is the strip's
and not the status's -- a status still starts in the column its tab's name
starts in, which is the rule the leading space existed for in the first place.
</content>
</entry>
<entry>
<title>osc 5577: `busy`, and a sidebar that shows it (busy_mark)</title>
<updated>2026-09-22T13:12:02Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-22T13:12:02Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=720aa48e249c0c962e8f73372ceded6092414a6e'/>
<id>urn:sha1:720aa48e249c0c962e8f73372ceded6092414a6e</id>
<content type='text'>
A status is a line of text, and a line of text cannot say whether it is still
true. "building 3/7" reads exactly the same while it builds and ten minutes
after it stopped, which is the one thing somebody reading it from another tab
wants to know -- and the only workaround available to a program was to write
the answer into the words and keep rewriting them.

So `busy`:

    printf '\033]5577;1;status;make test\033\\'
    printf '\033]5577;1;busy;1\033\\'
    ...
    printf '\033]5577;1;busy;0\033\\'

A flag of its own rather than a field of `status`, because a status is the
*whole* payload after the verb -- `status;a;b;c` has always been the text
"a;b;c" -- so anything added to that line would change what every existing
sender means, including the five pi extensions D1 promises byte compatibility
to. Orthogonal, so a program can leave one line up and toggle the other;
anything but 0/false/no/off is on; `clear` takes both; and a pane whose program
has gone is never busy, whatever it last claimed, because a spinner turning
under a dead tab is a lie you keep looking at.

In a sidebar the row turns `tab_status_busy` -- the theme's accent, derived
like the other status colours, so no theme needs a line -- and takes a spinner
in the column `tab_bar_padding` was already holding blank, so nothing reflows
when a program starts or stops working. `busy_mark` is the frames, separated by
spaces: one frame is a static mark and costs nothing, several make it turn, and
only then does the session keep a frame clock -- via the same `animating` flag
an animated shader uses, so the cost lands only while such a row is actually
composed, and `""` drops the mark entirely and leaves the colour to say it.
The frame is a pure function of the clock, like every other pulse here, so two
clients attached to one session show the same one.

`panes` now reports `status` and `busy`, which is the scriptable half of the
same fact: what a pane says about itself, without reading its screen.
</content>
</entry>
<entry>
<title>sidebar: air inside it, the way panes have padding (tab_bar_padding)</title>
<updated>2026-09-22T13:01:07Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-22T13:01:07Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=43ab0d0c0dc0c79f04c7e70f5ab3b22b3e36e09a'/>
<id>urn:sha1:43ab0d0c0dc0c79f04c7e70f5ab3b22b3e36e09a</id>
<content type='text'>
`padding` is a pane's: the ring between a frame and the text it holds, in rows
times gap_aspect so a `1` looks square around a block of prose. Handing the
same knob to the sidebar looked tempting and is wrong twice over. The sidebar
is chrome, not a pane, and its width is fixed by tab_bar_width -- so `padding
1` would quietly eat four of a sixteen-column strip (two cells a side, aspect
included) and shorten every tab name, because somebody wanted their terminal
text to breathe. And its two axes are not one idea: the columns are an indent
for labels, the rows are air above a list.

So tab_bar_padding, its own knob in the family the rest of the sidebar's air
already lives in (tab_bar_pad, tab_gap, newtab_pad), taking the same 1/2/4
CSS-order values as `padding` -- one reader for both, so they cannot drift
into meaning different things -- but counted in cells.

The default `0 1` is what the sidebar already drew: that one column is where
every label's leading space has been hard-coded since the sidebar existed, and
moving it into a setting is what makes `tab_bar_padding 0` possible at all --
names flush against the frame, which was not previously a thing anyone could
ask for. The statuses, the `+ new tab` row and the pane count follow the same
indent, since they were each carrying their own copy of that space.

It is air *inside* the plate: a tab's fill still spans the whole row and the
whole row is still the hit, because the row is what you click and a highlight
that stopped short of the frame would read as a highlighted word rather than a
chosen row. The bottom rows are given up when the strip is short of them, like
every other piece of air in here.

tab_bar_pad is the older name for the top component alone. It still works,
still means exactly that, and wins where a config writes both -- it is the
more specific of the two. The dump writes tab_bar_padding and mentions it.
</content>
</entry>
<entry>
<title>sidebar: align the numbers by default</title>
<updated>2026-09-22T12:49:40Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-22T12:49:40Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=9f751ab432ae74d4462fb4c3fefbd85415f47c86'/>
<id>urn:sha1:9f751ab432ae74d4462fb4c3fefbd85415f47c86</id>
<content type='text'>
The knob landed defaulting to "prefix" on the grounds that it is what the
strip has always drawn -- but the strip is not what this is about. A sidebar
row is wider than the name on it, and `1:api` spends the first two or three
cells of every row (three or four once you have ten tabs) on a number nobody
reads until they reach for C-a 2, pushing the names in by a distance that
depends on how many tabs are open. Right-aligned, the names start where the
statuses under them start and the numbers make a column of their own.

`tab_bar_index "prefix"` is still there and unchanged, and the top strip is
unaffected either way -- it ignores the setting, having no far end to align
against.

The tests that used `1:` as the anchor for "where a tab's name starts" now
anchor on the name, which is what they were about; two new ones cover the
default and the prefix form explicitly, so neither mode is tested only by
being the default.
</content>
</entry>
<entry>
<title>sidebar: the tab number at the far edge (tab_bar_index)</title>
<updated>2026-09-22T11:14:24Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-22T11:14:24Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=24bfcc26df376621ccfe1e695bb4f5e6f1807e56'/>
<id>urn:sha1:24bfcc26df376621ccfe1e695bb4f5e6f1807e56</id>
<content type='text'>
`1:api` puts a number at the start of every row, and in a sidebar that is a
column of digits in front of the thing you are actually reading -- the names
start one, two or three cells in depending on how many tabs you have, and the
numbers are only wanted at the moment you reach for C-a 2.

tab_bar_index "right" moves them:

    │ tests        1 │        │ 1:tests        │
    │ verylongproj 2 │   vs   │ 2:verylongproje│
    │ api          3 │        │ 3:api          │

Every name starts in the same column and the numbers line up in one of their
own, which costs a column of sidebar and buys the list a shape. "prefix" stays
the default, because it is what the strip has always drawn and the strip is
where most people meet it.

Sidebars only, and not by omission: a tab in the top strip is exactly as wide
as its label, so there is no far end to align against and a number floating
after a name it does not belong to would be worse than the prefix it replaced.
The setting is ignored there rather than half-honoured. A row too narrow to
spare the columns (under the number plus five) also keeps the prefix, since
the name is the part you came for, and the rename editor keeps the whole cell
as it always did.

A name long enough to reach the number stops one cell short of it, because
`verylongproje2` is a tab called verylongproje2 as far as any reader can tell.
The cell comes back as padding, so the plate is unbroken and the hit is still
the whole row -- clicking the number is clicking the tab, and the number
brightens with the label under the pointer rather than leaving half a row lit.
The bell mark keeps its place between them, budgeted first as before, so a
long name cannot eat either of the two things that are not the name.
</content>
</entry>
<entry>
<title>themes: ask the binary where it is, not the prefix it was built for</title>
<updated>2026-09-22T10:57:26Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-22T10:57:26Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=68ff62e9492accd4c1c9a83edbcbd225afb1a11a'/>
<id>urn:sha1:68ff62e9492accd4c1c9a83edbcbd225afb1a11a</id>
<content type='text'>
`theme_name "sl0p"` with all seven themes sitting in /usr/share/slosh/themes:
"no theme named sl0p". The lookup knew two places -- your own themes directory
and the PREFIX this build was compiled with -- and on that machine the first
did not exist and the second was /usr/local, because that is the Makefile's
default and the package was built with PREFIX=/usr. Neither was where the
themes actually were, and nothing in the search could ever have found them.

A compiled-in prefix is a guess about where the binary will end up, and the
binary is the one thing that knows where it really is. So the search now asks
it: path_self() (/proc/self/exe, _NSGetExecutablePath, GetModuleFileName --
lifted out of server.c, which was already doing this for SLOSH_BIN) gives
&lt;bin&gt;/../share/slosh/themes for an installed slosh wherever it was installed
to, and &lt;repo&gt;/contrib/themes for one being run out of a build tree, which
makes a checkout able to name a theme without installing anything first. The
compiled-in prefix stays as the last candidate, for a binary sitting somewhere
that says nothing about its data, and duplicates collapse so the usual install
does not report the same directory twice.

Your own directory is still first and still shadows everything, which is the
property that matters; the rest is about the shipped set being findable at all.

The tests that assumed a closed world of two temp themes now narrow to their
own names by typing, since the shipped ones are (correctly) always in the list
-- and the "no themes anywhere" toast lost its test, because with the shipped
set reachable that state takes a binary with nothing beside it. The case that
started this is a test of its own.
</content>
</entry>
</feed>
