diff options
| author | user <user@clank> | 2026-08-14 21:32:40 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-14 21:32:40 +0200 |
| commit | c5f4bb75403df3ca7e0bf6c30981c0cb4e54921e (patch) | |
| tree | 44bddd3b1ba1ea0f4782b50d9c095c6b813f7b7b /config | |
| parent | sl0ppty: let a child's exit status survive long enough to be read (diff) | |
| download | slosh-c5f4bb75403df3ca7e0bf6c30981c0cb4e54921e.tar.gz slosh-c5f4bb75403df3ca7e0bf6c30981c0cb4e54921e.tar.xz slosh-c5f4bb75403df3ca7e0bf6c30981c0cb4e54921e.zip | |
sl0ppty: a pane outlives the program that was in it
A pane whose program exited was reaped before the next paint. The pane went,
and with it everything the program had printed -- including, always, the one
line that would have explained why it stopped. Close the last one and the
session went too, so a mistyped command in a fresh session closed the
terminal you were typing in.
Now the pane stays. It keeps its contents, writes one line into its own
backlog saying how it ended -- `[process exited: status 3]`, `signal 9` --
says the same thing in its frame and in the status line, and offers the two
verbs that still mean anything:
╰ exited: status 3 ──────────────────────────────[re-run]─[close]─╯
`[re-run]` runs the same argv in the same cwd in the *same pane*: same id,
same node in the tree, same terminal. Nothing that referred to that pane has
to be told anything, and the run that ended stays above the new one in the
scrollback, which is what makes comparing two runs possible at all. C-a r
and {"cmd":"rerun"} do the same thing; a live pane is refused rather than
restarted, because "run it again" is not a request to kill something.
Those buttons are the OSC 5577 row, not a second mechanism. A dead pane's
own buttons are inert -- a click would write a report into a closed pty --
so the row is given over rather than added to. `close` is listed last, so it
is drawn rightmost and is the one that survives a narrow frame: being unable
to dismiss a dead pane is a trap, being unable to re-run one is an
inconvenience.
`dead` is a shader state now, which D13 said it could never be: it named a
frame that could not be drawn, and now there is one. Dead panes drain to
grey by default, so a wall of them reads at a glance. A collapsed header
says "exited" in words as well, because the shader pass does not reach one
and a flattened tab draws nothing else of a pane.
Two things about this are load-bearing rather than incidental:
- the pty fd is closed the moment EOF is seen. A pane that outlives its
program stays in the poll set, and an fd at EOF is readable forever: the
session would spin a core behind a pane that looks idle.
- the exit status is collected with a bounded wait, never a blocking one.
EOF means the program already closed its fds, so it is on its way out and
a few hundred microseconds wins that race. A blocking waitpid would hang
the whole session on one program that closes its terminal and keeps
running; losing the race costs the words "status 0" and nothing else.
`keep_dead false` restores the old behaviour for a one-shot layout that
wants it. `panes` reports alive/exit_code/exit_signal, so a script can find
the pane whose command failed and rerun it.
Diffstat (limited to 'config')
| -rw-r--r-- | config/config.kdl | 38 |
1 files changed, 33 insertions, 5 deletions
diff --git a/config/config.kdl b/config/config.kdl index db272a4..ef3c4f2 100644 --- a/config/config.kdl +++ b/config/config.kdl @@ -77,6 +77,18 @@ zoom_on_mark "*" close_mark "x" min_mark "_" +// A pane whose program exits stays where it is, keeping everything it +// printed, with a line in its own backlog saying why it ended and two buttons +// in its frame: [re-run] to run the same command again in the same pane and +// on top of the same scrollback, and [close] to let it go. `sl0ppty` closes +// with the last pane, so a session no longer disappears the instant the shell +// in it exits. +// +// false is the old behaviour: the pane is closed on the next paint, and with +// the last pane the session. That suits a one-shot layout, and costs you the +// last thing a failing command printed. +keep_dead true + bell_indicator true // What to draw. A short string rather than a fixed glyph, because the obvious @@ -161,6 +173,7 @@ double_click_ms 400 // dragging the pane you have hold of // drop_hover the one under the pointer, where it would land // drop_target the others, all of them somewhere it could go +// dead its program exited; waiting to be re-run or closed // suspended laid out, never started (its label is its contents) // scrolled looking at scrollback rather than at the present // unfocused the weakest thing that can be true of a pane @@ -174,13 +187,13 @@ double_click_ms 400 // Naming a state replaces its default outright — including with nothing, which // is how you turn one off: states { drop_target { } } // -// There is deliberately no `dead`: a pane whose program exits is reaped before -// the next frame is painted, so a shader for it could never draw. -// -// Only drop_hover and drop_target have defaults, shown here. +// Only drop_hover, drop_target and dead have defaults, shown here. `dead` is +// gentler than the drag greying on purpose: a dead pane is something you are +// still meant to read. states { drop_hover { grayscale amount=200; dim amount=140 } drop_target { grayscale amount=200; dim amount=140 } + dead { grayscale amount=200; dim amount=90 } // dragging { } // suspended { grayscale amount=180; dim amount=90 } // scrolled { tint amount=30 color="#ffcc88" } @@ -281,6 +294,18 @@ theme { // ── the line along the bottom ─────────────────────────────────────────── status "#45454a" // session, tab, pane, and the pane index status_state "#ffffff" // scrolled / alt screen / not started / exited + hint "#ffffff" // what the pointer is on, in the middle + + // ── marks on a pane's frame ───────────────────────────────────────────── + bell "#ff5fd7" // a pane rang and has not been looked at + dead "#ff875f" // its program exited: the line in its backlog, + // and the words in its frame and the status line + pane_button "#45454a" // the frame's own close/zoom/minimise marks + pane_button_hover "#ff5fd7" + + // ── the row of minimised panes ────────────────────────────────────────── + minbar "#45454a" + minbar_hover "#ff5fd7" // ── the pane finder ───────────────────────────────────────────────────── finder_fg "#ffffff" @@ -307,7 +332,7 @@ theme { // Actions: split-cols split-rows close-pane focus-left focus-right focus-up // focus-down focus-next resize-left resize-right resize-up resize-down // scroll-up scroll-down scroll-page-up scroll-page-down scroll-top -// scroll-bottom zoom minimize new-tab next-tab prev-tab select-tab-1..9 finder detach quit +// scroll-bottom zoom minimize rerun new-tab next-tab prev-tab select-tab-1..9 finder detach quit // literal-prefix none keys { @@ -316,6 +341,9 @@ keys { bind "\\" "split-cols" bind "-" "split-rows" bind "x" "close-pane" + // Run a dead pane's command again, in the same pane and on top of the + // same scrollback. Refused on a pane that is still running. + bind "r" "rerun" bind "z" "zoom" bind "m" "minimize" |
