<feed xmlns='http://www.w3.org/2005/Atom'>
<title>slosh.git/include/slosh.h, branch v0.1.0</title>
<subtitle>an opinionated terminal multiplexer in C with programmable pane chrome</subtitle>
<id>https://git.sl0p.foo/slosh.git/atom/include/slosh.h?h=v0.1.0</id>
<link rel='self' href='https://git.sl0p.foo/slosh.git/atom/include/slosh.h?h=v0.1.0'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/'/>
<updated>2026-08-27T13:09:02Z</updated>
<entry>
<title>selection: drag past an edge scrolls the viewport under it</title>
<updated>2026-08-27T13:09:02Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-27T13:09:02Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=2ee881452aa4b19701a071f0333a15265e3b314e'/>
<id>urn:sha1:2ee881452aa4b19701a071f0333a15265e3b314e</id>
<content type='text'>
Text taller than the viewport was not selectable in one gesture: the
drag stopped dead at the pane's border. Two faults, one visible and one
waiting. The visible one: DRAG_SELECT motion subtracted the content
origin in uint16_t, so a pointer above or left of the pane underflowed,
the grid lookup failed, and the selection froze. The waiting one: the
anchor was a pair of *viewport* coordinates re-resolved on every extend,
so the moment anything scrolled mid-drag the anchor would have drifted
with the viewport, re-anchoring to whatever slid under the press.

The anchor is now a tracked grid ref -- the thing lib-vt documents as
being for 'long-lived anchors such as selections' -- so it follows its
cell across scrolling, pruning and reflow, and only the near end of the
selection moves. If a program flooding output prunes the anchored row
out of scrollback entirely, the selection keeps what it had; there is no
cell left to anchor a new answer to.

Motion clamps into the content rect (a pointer past an edge is pointing
at the nearest cell), and holding past the top or bottom edge scrolls on
a clock rather than on motion, because a resting pointer sends no events
and 'keep going' is exactly what resting it there means. app_tick() does
the step, app_next_deadline_ms() wakes the poll loop for it -- the same
mechanism toasts and hover guides already use -- and both front ends
call the tick, so the behaviour exists under the server and the headless
harness alike. Rows per step is how far past the edge the pointer sits:
one row past creeps, further hurries, so the hand already dragging is
also the throttle. The metronome is select_scroll_ms, a knob so the test
can configure the clock down instead of sleeping.

The test drags to one row above the pane and simply holds: the viewport
walks to the top of scrollback (clamping makes the endpoint exact), the
copy starts at a line that was never on screen with the press, and still
ends at the pressed row -- that last check is the tracked anchor's.
</content>
</entry>
<entry>
<title>slosh: OSC 8 hyperlinks pass through the compositor</title>
<updated>2026-08-20T21:23:07Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-20T21:23:07Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=965966f9fb3808fbc69e6cb55960ad7a3c72c5f4'/>
<id>urn:sha1:965966f9fb3808fbc69e6cb55960ad7a3c72c5f4</id>
<content type='text'>
lib-vt tracks a hyperlink per cell and slosh dropped every one of them: the
client's terminal can only offer a link it was sent, so `ls --hyperlink`,
gcc diagnostics and friends silently lost theirs inside the session.

The link rides beside the cell -- cell_t is the shader ABI and a link is
not a colour -- as a plane of ids into an interned URI table, append-only
so an id in prev_link still means the same URI next frame (which is what
lets the diff compare ids instead of strings), and capped so a hostile pane
cannot grow it forever. URIs with control bytes are refused outright: they
are emitted verbatim inside an OSC, and a control byte in one is a way to
write escape sequences into the client's terminal. The diff wraps emitted
runs in OSC 8, closes what it opens before the frame ends, and treats a
link-only change as a change. Chrome painted over a link takes the link
with it, so a frame, modal or float across a URL is never clickable with
the pane's link; links survive scrollback with the text that carries them.

Plain-text URLs stay the outer terminal's own matcher, with the catch
documented in panes.md: under mouse capture Ghostty only offers links with
shift added to the gesture (shift+cmd hover/click), shift being its
capture escape. Wrapped URLs can never be regex-matched through a
multiplexer -- rows repaint as hard lines -- which is exactly what OSC 8
links, passing through whole, are for.

`snapshot format:"bytes"` returns the emitter's own output for a frame (a
second call is the delta): the model and the bytes are two different
things, and this codebase already paid once for asserting on one while the
other was wrong.
</content>
</entry>
<entry>
<title>slosh: defaults most people can live in, said by shaders</title>
<updated>2026-08-19T11:46:57Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-19T11:46:57Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=42b3c10995eb67778d9dd3aa2fd80624896acd5f'/>
<id>urn:sha1:42b3c10995eb67778d9dd3aa2fd80624896acd5f</id>
<content type='text'>
The out-of-the-box look, rethought as one piece:

- the compiled-in palette calms down: muted blue #7aa2f7 on the same
  neutral darks, with the colours that mean something kept semantic --
  amber bell, red-orange dead. sl0p stays one include away as the house
  style, and contrib/themes/default.kdl is the starting point written
  down (theme-tour shows it).

- derived state defaults now actually follow the theme: the scrolled
  wash is rebuilt from scroll_bg after the theme block (it used to bake
  the compiled accent), and declaring a state -- in any file of an
  include chain -- still wins. The dump writes derived states as
  comments so C-a e cannot pin what is meant to keep following.

- a bell you can see: the frame blinks three times in theme's bell
  colour then breathes until answered, and a soft diagonal shimmer --
  the bell colour halved towards default_fg, width breathing on a sine
  -- crosses the body for the same 900ms. bell_indicator false silences
  all of it; states { bell { } } replaces it like any state. since
  carries the clock dependency, so an idle session pays nothing.

- scrollback says where it continues: while scrolled, the viewport's
  top and bottom rows melt towards an edge with content past it and
  turn solid at the buffer's end. New expression variables above/below
  (lines hidden past each edge, own dependency bit, cached per scroll
  position), carried in shade_ctx_t -- SLOSH_SHADER_ABI 3 -&gt; 4.

- expressions survive the dump: a program keeps its source and cb_chain
  writes it back, so amount="(y % 2) * 40" no longer dumps as a number.

- headers move to include/, -Iinclude everywhere; the plugin Makefile's
  SLOSH var becomes SLOSH_DIR because slosh exports SLOSH=1 into every
  pane it runs, and building from inside a session read that as a path.

- comment drift culled where features outran the prose, and the
  derivation boilerplate folded into derived_pass()/ctx_scroll().
</content>
</entry>
</feed>
