aboutsummaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
authoruser <user@clank>2026-09-22 12:42:15 +0200
committeruser <user@clank>2026-09-22 12:42:15 +0200
commitfaa3fb6ee575a900abc574b0f9ed5ff291761837 (patch)
treece644998c144687e205d1f4e97e6f999de98723b /contrib
parenttabs: idle tabs you can actually read across the room (diff)
downloadslosh-faa3fb6ee575a900abc574b0f9ed5ff291761837.tar.gz
slosh-faa3fb6ee575a900abc574b0f9ed5ff291761837.tar.xz
slosh-faa3fb6ee575a900abc574b0f9ed5ff291761837.zip
theme: a name and a directory, not sixty colours in your config
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 <prefix>/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 `&c->`, 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.
Diffstat (limited to 'contrib')
-rwxr-xr-xcontrib/theme-tour26
-rw-r--r--contrib/themes/default.kdl150
2 files changed, 114 insertions, 62 deletions
diff --git a/contrib/theme-tour b/contrib/theme-tour
index d816cfe..3b4a4ce 100755
--- a/contrib/theme-tour
+++ b/contrib/theme-tour
@@ -5,9 +5,13 @@
# contrib/theme-tour slate apply one to the session named below
# SESSION=work contrib/theme-tour ...to a different session
#
-# It works by writing the theme to the file the session was started with and
-# letting the config watcher do the rest, which is also a fair demonstration of
-# the watcher.
+# One control-socket call per theme (`cmd theme`), which is the same thing a
+# person does with one line of their config -- the tour used to copy a theme
+# file over the session's config and wait for the watcher to notice, which
+# demonstrated the watcher rather than the themes.
+#
+# THEME_DIR points the session at this repo's themes, so the tour shows what is
+# in the checkout rather than what happens to be installed.
set -e
here=$(cd "$(dirname "$0")" && pwd)
bin="$here/../build/slosh"
@@ -16,23 +20,23 @@ CONF=${SLOSH_CONFIG:-/tmp/slosh-theme-tour.kdl}
DWELL=${DWELL:-4}
apply() {
- cp "$here/themes/$1.kdl" "$CONF"
+ "$bin" -s "$SESSION" cmd "theme $1" >/dev/null
printf ' %-10s %s\n' "$1" "$(sed -n '1s|// ||p' "$here/themes/$1.kdl")"
}
-if [ -n "$1" ]; then
- [ -f "$here/themes/$1.kdl" ] || { echo "no such theme: $1"; exit 1; }
- apply "$1"
- exit 0
-fi
-
if ! "$bin" ls 2>/dev/null | grep -q "^$SESSION "; then
- cp "$here/themes/sl0p.kdl" "$CONF"
+ printf 'theme_dir "%s"\ntheme_name "sl0p"\n' "$here/themes" >"$CONF"
echo "starting a session called $SESSION..."
SLOSH_CONFIG="$CONF" "$bin" -s "$SESSION" -- "${SHELL:-/bin/sh}" &
sleep 1
fi
+if [ -n "$1" ]; then
+ [ -f "$here/themes/$1.kdl" ] || { echo "no such theme: $1"; exit 1; }
+ apply "$1"
+ exit 0
+fi
+
echo "cycling every theme, ${DWELL}s each. ^C to stop wherever you like."
for t in "$here"/themes/*.kdl; do
apply "$(basename "$t" .kdl)"
diff --git a/contrib/themes/default.kdl b/contrib/themes/default.kdl
index 5924e82..fdd4479 100644
--- a/contrib/themes/default.kdl
+++ b/contrib/themes/default.kdl
@@ -1,91 +1,139 @@
// default -- what no config gets you: muted blue on neutral dark
//
-// This file exists so the tour can show the starting point and so another
-// theme is one include-swap away from coming back. It matches the compiled-in
-// palette exactly (`slosh --dump-config` is the authority if they ever drift).
+// The reference theme, and the annotated one: every colour the compositor
+// draws is named here, with what it is for. Copy this file to
+// ~/.config/slosh/themes/mine.kdl, change what you like, and put
+// `theme_name "mine"` in your config -- or name one of the others beside it.
+//
+// It matches the compiled-in palette exactly, so `theme_name "default"` and
+// naming no theme at all are the same session (`slosh --dump-config` is the
+// authority if they ever drift).
+//
+// The names started as six doing thirty jobs, which meant the split guide
+// could not be recoloured without also recolouring the focused frame, because
+// they were the same entry. Several still share a value: that says they look
+// right together, not that they are the same thing.
//
// Two colours mean something rather than match something: `bell` is amber
// (attention, not focus) and `dead` is warm red-orange. Everything else that
// says "this one" -- the focused frame, the guide, the active tab -- is one
// muted blue, calm enough to frame text without shouting over it.
theme {
- // what a shader takes "terminal default" to mean
+ // What a cell's "terminal default" colour is taken to be when a shader has
+ // to compute on it. See the shaders chapter (docs/shaders.md).
default_fg "#ffffff"
default_bg "#000000"
- // panes
- frame_focus "#7aa2f7"
- frame_idle "#45454a"
- title "#ffffff"
- title_idle "#45454a"
+ // ── panes ───────────────────────────────────────────────────────────────
+ frame_focus "#7aa2f7" // border of the focused pane
+ frame_idle "#45454a" // border of every other pane
+ title "#ffffff" // its title, and its OSC 5577 status text
+ title_idle "#45454a" // the same, on a pane that is not focused
- // a pane's own OSC 5577 buttons
+ // A pane's own OSC 5577 buttons.
button_fg "#141418"
button_bg "#7aa2f7"
- button_bg_idle "#55555c"
+ button_bg_idle "#55555c" // buttons on a pane that is not focused
- // affordances under the pointer
- guide "#7aa2f7"
- resize "#7aa2f7"
- drop_target "#7aa2f7"
+ // ── things that appear under the pointer ────────────────────────────────
+ guide "#7aa2f7" // split guide: armed edge, handle, its arrow, boundary
+ resize "#7aa2f7" // the handle in the gap between two panes
+ drop_target "#7aa2f7" // frame of a pane a drag could land on
- // scrollback indicator
+ // ── a pane's scrollback indicator ───────────────────────────────────────
scroll_fg "#141418"
scroll_bg "#7aa2f7"
- // rows of a tab collapsed into a list
+ // ── rows of a tab that has collapsed into a list ────────────────────────
header "#45454a"
- header_hover "#7aa2f7"
- header_hover_title "#ffffff"
+ header_hover "#7aa2f7" // the rule, under the pointer
+ header_hover_title "#ffffff" // the name, under the pointer
- // the strip along the top, or the sidebar down the side
+ // ── the strip along the top ─────────────────────────────────────────────
+ // The tab you are in is filled rather than merely bold: bold alone is easy
+ // to miss on a strip of short labels. It keeps that fill while the pointer
+ // is on it -- it is still the tab you are in -- so its hover lands on the
+ // text instead.
tab_active_fg "#141418"
tab_active_bg "#7aa2f7"
tab_active_hover_fg "#ffffff"
+ // Brighter than the grey on idle frames and titles: a tab you are not in
+ // is a place to go, and has to read from across the screen.
tab_idle "#8a8a95"
+ // Every other tab is a plate too, 43% of the way from the background to
+ // the grey above: a tab with edges rather than a word floating on the
+ // background, and something for the active fill to be brighter *than*.
+ // That fraction is a contrast ratio in disguise -- about 1.9:1 against
+ // the background whether the theme is dark or light. Unset, it is mixed
+ // from those two, so a theme gets one in its own family without naming
+ // it. Name your own background here for bare tabs.
+ tab_idle_bg "#3b3b40"
tab_hover "#7aa2f7"
- prefix_fg "#141418"
+ prefix_fg "#141418" // the "C-a" badge, while the prefix is pending
prefix_bg "#7aa2f7"
- tab_count "#45454a"
- // a sidebar's status rows, part way from tab_count to tab_hover: the live
- // thing in the sidebar, so not the flat grey of the chrome round it, and
- // not the full accent either. Unset, it is mixed from those two, so a
- // theme gets a status colour in its own family without naming one.
- // tab_status_bg is unset -- a band is opt-in, and none leaves a
- // translucent terminal's background alone.
- tab_status_fg "#5a6a8f"
+ tab_count "#45454a" // panes in the whole session
+
+ // ── 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
- // the line along the bottom
- status "#45454a"
- status_state "#ffffff"
- hint "#ffffff"
+ // ── marks on a pane's frame ─────────────────────────────────────────────
+ bell "#e0af68" // a pane rang and has not been looked at:
+ // amber, not the accent -- attention, not focus
+ 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 "#7aa2f7"
+
+ // ── the row of minimised panes ──────────────────────────────────────────
+ minbar "#45454a"
+ minbar_hover "#7aa2f7"
+
+ // ── modals: a surface that floats over the layout ───────────────────────
+ // Named apart from the pane colours they resemble because a modal is a
+ // different surface: opaque, on top of everything, and legible against a
+ // background that has been dimmed underneath it. (The first version drew
+ // the close button in the pane button colour on the finder background,
+ // which are the same value — an invisible button until you hovered it.)
+ modal_bg "#1c1c22" // a shade lighter than the dimmed screen
+ modal_fg "#e8e8ea"
+ modal_border "#7aa2f7"
+ modal_title "#ffffff"
+ modal_button "#8a8a95" // visible *without* the pointer on it
+ modal_button_hover "#7aa2f7"
- // the pane finder
+ // ── the pane finder ─────────────────────────────────────────────────────
+ // The finder is a modal, so its box is the modal colours above; these are
+ // the parts only it has. `finder_bg` is the query field and the row under
+ // the pointer — a band that is not the box, so typing and hovering both
+ // have somewhere to show.
finder_fg "#ffffff"
finder_bg "#45454a"
finder_sel_fg "#141418"
- finder_sel_bg "#7aa2f7"
+ finder_sel_bg "#7aa2f7" // the row enter would take
+
+ // ── scrollback search (the `/` bar) ────────────────────────────────
+ // Every match on screen is tinted `search_*`; the one the bar is on gets
+ // `search_cur_*` instead, so "this one" reads apart from "all of them".
+ // Warm gold by default, because a match is a different thing from a
+ // selection (finder) or focus (accent) and should not be mistaken for one.
+ search_fg "#ffffff"
+ search_bg "#4d4124" // the other matches
+ search_cur_fg "#141418"
+ search_cur_bg "#f2c97a" // the one ↑↓ and enter act on
- // rename editor, bell, dead panes, frame buttons, the minimised bar
+ // ── the in-place editor a double-click opens on a pane title ────────────
rename_fg "#141418"
rename_bg "#7aa2f7"
+
+ // ── announcements ───────────────────────────────────────────────────────
toast_fg "#141418"
toast_bg "#7aa2f7"
- // the "N clients" tag: the theme's muted chrome tone, quiet on purpose
+ // The "2 clients" tag when several terminals share the session. Muted by
+ // default -- standing information, not an announcement. attach_bg unset
+ // means the terminal's own background.
attach_fg "#45454a"
- bell "#e0af68"
- dead "#ff875f"
- pane_button "#45454a"
- pane_button_hover "#7aa2f7"
- minbar "#45454a"
- minbar_hover "#7aa2f7"
-
- // a modal floating over the layout (the cheatsheet, C-a ?)
- modal_bg "#1c1c22"
- modal_fg "#e8e8ea"
- modal_border "#7aa2f7"
- modal_title "#ffffff"
- modal_button "#8a8a95"
- modal_button_hover "#7aa2f7"
+ // attach_bg "#141418"
}