aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authorPeter Geissler <peter@haxx.in>2026-09-02 12:32:45 +0200
committerPeter Geissler <peter@haxx.in>2026-09-02 12:32:45 +0200
commit794e22914666775cab0b1b730a000b2529cec6b5 (patch)
tree77cb83a5f6417d92bc21e3f7ea0a49be45b2ac9b /docs
parentdocs: big overhaul (diff)
downloadslosh-794e22914666775cab0b1b730a000b2529cec6b5.tar.gz
slosh-794e22914666775cab0b1b730a000b2529cec6b5.tar.xz
slosh-794e22914666775cab0b1b730a000b2529cec6b5.zip
docs: deprecate boring design narrative, more trimming and culling
Diffstat (limited to 'docs')
-rw-r--r--docs/chrome.md55
-rw-r--r--docs/design.md107
-rw-r--r--docs/index.md1
-rw-r--r--docs/layouts.md150
-rw-r--r--docs/nav1
-rw-r--r--docs/workspaces.md125
6 files changed, 35 insertions, 404 deletions
diff --git a/docs/chrome.md b/docs/chrome.md
index 1450dcb..45b223a 100644
--- a/docs/chrome.md
+++ b/docs/chrome.md
@@ -15,33 +15,13 @@ Nothing else changes: same shaders, same `amount`, same
[expressions](shaders.md#strength-as-an-expression), same chain order. One new
word.
-You have already seen one: the default bell flash (a blink, then a breathe on
-the frame of a pane that rang, in theme's `bell` colour) is exactly this, a
-chrome tint whose amount reads `since`. (The shimmer that crosses the pane's
-body at the same moment is the same idea without the new word: an ordinary
-content pass in the same chain.) Writing your own `bell { … }` replaces both;
-`bell_indicator false` silences them with the mark.
-
-## Two rules
-
-**`channel="fg"`, almost always.** A border's *background* is the terminal's own
-default; mixing that towards a colour paints a rectangle behind the glyphs
-instead of colouring them, and leaves it there until the pane changes state.
-`fg` also keeps the padding inside the border out of it, since a blank cell has
-no foreground to colour, so the effect lands on exactly the rules, the title and
-the buttons.
-
-**The contents are cut out of the pass.** A full-strength chrome tint cannot
-reach your text, and a full-strength content tint cannot reach the border. They
-are two rects and two passes.
-
## Positions are the whole frame's
`x`/`y` are counted from the frame's top-left corner, with `cols`/`rows` the size
of the pane's rect, not of one side. One pass over the whole ring, so an effect
can travel round a border instead of restarting at every corner.
-For anything that *travels*, though, the coordinate you want is distance along
+For anything that _travels_, though, the coordinate you want is distance along
the ring, which the language can work out. Clockwise from the top-left:
```
@@ -49,17 +29,6 @@ the ring, which the language can work out. Clockwise from the top-left:
: y == rows-1 ? 2 * cols + rows - 3 - x : 2 * cols + 2 * rows - 4 - y)
```
-with the perimeter itself `2 * cols + 2 * rows - 4`. Three things fall out of it:
-
-- `min(d, P - d)` is distance the short way round, so an effect crosses the
- corner it started from instead of stopping at a seam.
-- `(p - head + P) % P` is "how far ahead of the head", which makes an arc rotate
- rather than a blob slide.
-- `sin(TAU * p / P)` is that position **as an angle**, one cycle to the lap, and
- a sine wraps at the corner for free, where a ramp needs the trick above. Any
- whole number of lobes (`* 3 / P`) fits the ring exactly, which is what makes a
- standing wave stand.
-
There is no way to name a subexpression, so the formula is written out in full,
which is why a small effect is a long line.
@@ -67,15 +36,12 @@ Thirteen of them are ready to paste in
`contrib/chrome`:
a shine running round the border, a sine comet with softer shoulders, marching
ants, a standing wave, drifting sparkles, ripples leaving a corner in both
-directions, a rotating arc, a bit-drift pattern, a heartbeat, a static gradient,
-corner glints, a sheen across the title rule and an XOR barcode along it. Its
-README has the two techniques they share: the angle idiom above, and overdrive
-then clip (`* 14 - 3000` after a sine) to turn a glow into an arc.
+directions, etc.
## A border that says something
-Hung off a [state](config.md#pane-states), this is chrome doing the job chrome is
-for, in no room and no words:
+Depending on a pane's [state](config.md#pane-states), we can visually communicate
+through it's chrome:
```kdl
states {
@@ -88,21 +54,14 @@ states {
}
```
-`since` is what makes a *flash* possible: it is how long the pane has been in
+`since` is what makes a _flash_ possible: it is how long the pane has been in
this state, where `t` only knows what time it is. The `bell` state ends when you
-look at the pane, because that is what answering a bell is.
-
-A pane that a small terminal has collapsed to a single row is chrome all the way
-through, which makes this the only kind of pass that can still colour it. That
-is the state where a colour is worth the most.
+look at the pane, because that is what answering a bell is. :-)
## Animation costs a clock
Anything that reads `t` or `since` asks the session to keep painting, at
-`anim_ms` (50ms, so 20fps) and only while such a pass is actually on screen. A
-pulse hung off `dead` costs a frame clock while a pane is dead and nothing for
-the rest of the session; the same pass in the global `shaders` block costs it for
-the whole session.
+`anim_ms` (50ms, so 20fps) and only while such a pass is actually on screen.
`anim_ms 0` turns the clock off, which leaves an animated shader advancing only
when something else causes a frame.
diff --git a/docs/design.md b/docs/design.md
deleted file mode 100644
index 458858c..0000000
--- a/docs/design.md
+++ /dev/null
@@ -1,107 +0,0 @@
-# How it works
-
-Two decisions do most of the work; the rest follow from them.
-
-## One geometry
-
-**Everything drawn registers what it is, as it is drawn**, and a click is a
-lookup in that list. Drawing and hit-testing cannot disagree, because there is
-only one of them. The dashed guide that shows where a split would land is drawn
-from the same rect the click will resolve against, in the same pass, so a
-preview that lies is not a bug you can have.
-
-The same rule applies to state that could be derived: the layout is a pure
-function of the tree and the terminal's size, recomputed every frame. Nothing
-stores where a pane is. That is why a small terminal can collapse a tab to a list
-of headers and come back to exactly the layout you had, and why a pane cannot be
-left greyed out by the one code path that forgot to clear a flag.
-
-Sizes are **weights**, so an even split is equal weights and resizing is not a
-special case of anything.
-
-## It runs headless
-
-`slosh --script` is the whole thing without a terminal: commands in, the
-composited screen out as JSON. The interactive client and the test suite drive
-the same object through the same commands, so there is exactly one implementation
-of what a key does.
-
-That is what makes the tests real: they type keys and assert on the screen a
-client would have seen, rather than on the internals. 1,500-odd checks in about
-thirteen seconds, plus 260 more from unit tests on the parts that are pure
-functions (the KDL parser, the shader pass, the expression compiler).
-
-## The shape of it
-
-```
-src/app.c the session core: pane and tab lifecycle, input routing
-src/app_layout.c the layout: splits, tree edits, resizing, focus
-src/app_draw.c drawing the chrome, the shader passes, graphics
-src/app_ui.c the finder, the palette, modals, renaming
-src/app_session.c layout files, workspaces, dumping a session out
-src/pane.c one pty + one libghostty-vt terminal, composed into the screen
-src/screen.c our cell buffer, and the minimal byte stream to update a terminal
-src/server.c the session process: socket, poll loop, config watcher
-src/client.c the attached terminal: bytes in, bytes out
-src/config.c the config, the keymap, the cheatsheet's names
-src/shader.c the colour passes; src/expr.c compiles their strengths
-src/kdl.c a hand-rolled KDL subset, for configs and layouts
-```
-
-About 15k lines of C. The vendored terminal core does VT parsing, scrollback,
-selection, images and key encoding; everything above is ours.
-
-## How it is formatted
-
-`clang-format` for the C (`.clang-format`) and `ruff` for the Python
-(`ruff.toml`), imports sorted and then laid out. `make fmt` applies both,
-`make fmt-check` only reports, and `make hooks` installs a pre-commit hook that
-formats what you staged, so what lands in git is already formatted.
-
-Getting the two tools:
-
-```
-make tools # a venv in .venv with the pinned ruff
-apt install clang-format # ...or brew, or your package manager
-```
-
-clang-format is a system package everywhere. ruff usually is not, and
-`pip install --user ruff` is refused outright on a distro python (PEP 668), so
-`make tools` builds a venv in `.venv/` and everything looks there **before**
-`$PATH`. That order matters: `ruff.toml` pins the version with ruff's own
-`required-version`, so a system ruff of the wrong one refuses to run, and
-preferring `$PATH` would strand somebody whose `.venv` has exactly the version the
-repo asked for. The venv survives `make clean`; `rm -rf .venv` starts over.
-
-The pin is there because a formatter whose output moves between releases turns
-"formatted" into "formatted by whoever committed last", and the diff lands on the
-next person. A mismatch is refused with both versions named rather than quietly
-reformatting the tree. Bumping it is one line in `ruff.toml`, one in the Makefile,
-and a reformat commit, which is the honest cost of a new version.
-
-The base is LLVM, because that is what this code was written in by hand: two
-spaces, 80 columns, `char *p`, braces attached. The settings that differ from it
-were chosen by measuring the reformat rather than by taste: LLVM alone rewrote 38%
-of the lines, mostly by splitting `if (!p) return false;` in two and reflowing
-comments that are prose. Keeping those two and leaving include order alone brings
-it to 17%, which is alignment and table packing.
-
-Comments are not reflowed. They are prose here, wrapped by hand, and sometimes the
-line breaks carry meaning: a paragraph, a list, a diagram of a pane. A comment
-that runs long is a thing for a person to fix.
-
-The Python is the opposite: left entirely at ruff's defaults. The C here was
-hand-formatted into a house style worth arguing for; the Python was not, and
-every knob turned would be an argument to have again later. It cost a 60%
-reformat of the test suite, once.
-
-**Python imports are sorted and C includes are not**, which is not an
-inconsistency. A C include order can be load-bearing (`_GNU_SOURCE` ahead of the
-system headers, a grouping somebody wrote by hand) while Python import order
-is not. Where it genuinely is, a statement between the imports ends the block ruff
-will sort, and the tests lean on exactly that: `sys.path.insert(...)` sits between
-the standard library and `from harness import ...`, so the harness import cannot be
-hoisted above the line that makes it importable.
-
-The vendored terminal core is never formatted: it is pinned by commit (D12), and
-reformatting it would turn every re-vendor into a merge.
diff --git a/docs/index.md b/docs/index.md
index c46eb7a..a40d11b 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -77,6 +77,5 @@ goes away. Reattach with the same command.
- **[Layouts](layouts.md)**: a session as a file you check in.
- **[Workspaces](workspaces.md)**: projects on disk, opened by name.
- **[Scripting](scripting.md)**: the control socket, and what a pane can draw.
-- **[How it works](design.md)**: the two decisions that shape the rest.
About 15k lines of C, no dependencies beyond libc and the vendored core.
diff --git a/docs/layouts.md b/docs/layouts.md
index 3818e77..0aae0d8 100644
--- a/docs/layouts.md
+++ b/docs/layouts.md
@@ -1,7 +1,6 @@
# Layouts
-A session can be a file, so a project's window layout is checked in with the
-project.
+A project's window layout can be defined programatiicaly (and checked in/managed with version control):
```kdl
layout {
@@ -22,29 +21,15 @@ slosh --layout project.layout
## What to call them
**`*.layout`**, and a project's is `slosh.layout`. The syntax is the same KDL
-subset the config uses — one parser reads both files — but the extension is
-not `.kdl`: zellij speaks KDL layouts with a different schema, and naming the
-wrong owner is worse than not naming the parser. Point your editor's KDL
-highlighting at `*.layout`. The old `*.layout.kdl` spelling is still found by
-the project scan, never written.
+subset the config uses. Point your editor's KDL highlighting at `*.layout`.
-A filename cannot enforce anything, so **the document decides which schema it is
-held to, not the flag that read it**. `slosh --check` reads what is in front of
-it: a file with `layout` or `tab` at the top is checked as a layout, a config
-handed to the same flag is still checked as a config, and a file too broken to
-parse at all is judged by its name, there being nothing else left to read.
+## How to verify them
```
$ slosh --check project.layout
project.layout: ok, a layout
```
-A clean layout says which schema it passed, because otherwise a layout nothing
-could open and a layout with nothing wrong in it read the same. A broken one
-reports every problem it found, one indented `file:line: what` per line, then a
-summary, and exits 1: the shape [checking a config](config.md#checking-one)
-already has, so one editor compile step and one pre-commit hook read both:
-
```
$ slosh --check broken.layout
broken.layout:2: unknown pane property: cmd
@@ -70,7 +55,7 @@ slosh: config.kdl: this is a config, not a layout: `gap` is a setting
A config handed to `--layout` is named for what it is instead of refused for
having no tabs, which is true of it in the least useful way.
-## The shape
+## The layout nodes
- `tab`: one per tab, in strip order. `name=`, `cwd=`, `purpose=`,
`active=true` for the one you land in.
@@ -84,18 +69,12 @@ having no tabs, which is true of it in the least useful way.
_would_ run.
- `focus=true`: the pane you start in, within its tab.
- `floating=true`: the pane starts [floating](panes.md#floating-a-pane), with
- `x=` `y=` `w=` `h=` as its wanted rect in cells. All four absent takes the
- centred default; `w=` `h=` alone is that size, centred — and centred again
- on every resize, until an explicit move or resize pins it. The rect is
- intent: a smaller screen clamps it, and the place you asked for survives. It still holds a seat in the tree: its `weight=` and
- position are where un-floating lands it. A file whose every pane floats
- lands one as the backdrop, because an overlay needs something to be over.
+ `x=` `y=` `w=` `h=` as its wanted rect in cells. All four absent takes the centred default.
- `purpose=`: a label for tooling; see below.
**A relative `cwd=` in a layout _file_ resolves against that file's own
directory**, never against the directory you started the session from. It is
-the rule [`include`](config.md#include) already follows, applied to the other
-half of the same syntax. A layout with no `cwd=` anywhere
+the same rule [`include`](config.md#include) already follows. A layout with no `cwd=` anywhere
starts in the file's directory too, so the common case needs no `cwd=` at all. An
absolute path is unchanged, and `~` is still your home directory.
@@ -115,122 +94,7 @@ layout {
}
```
-Clone that repo somewhere else and the tab still opens on the checkout, with the
-pane that said `src` still in the checkout's `src`.
-
-A layout that arrives as _text_ over the socket has no file, and so no
-directory to be relative to; relative paths in it resolve against wherever the
-session is.
-
-A full annotated example is
-`config/example.layout`.
-
-## Writing one back out
-
-```bash
-slosh -s work cmd '{"cmd":"dump-layout"}' > project.layout
-```
-
-writes the session as a layout file: tabs, splits, proportions, directories,
-commands, which pane you were in. So a session can be checked in, or put back
-after a restart.
-`contrib/slosh-dev`
-is that loop, for when the thing you are rebuilding is slosh itself.
-
-**A pane is dumped with the command it is running**, whether a layout gave it one
-or you typed it. `label`, what a layout said, comes first and outlives the
-program that ran it, so a project's `command="npm run dev"` stays that in the file
-after the server has exited. Failing that, the pane's terminal is asked what owns
-it: a pty has a foreground process group, and that group _is_ the job running in
-the pane. So arranging a project by hand and writing it down are one job rather
-than two: split, start the dev server and the log tailer, `C-a W`, done.
-
-Three things are deliberately not a command. **A shell at a prompt**, because
-`command="zsh"` on every idle pane would restore a session where each shell runs
-inside a shell. **A background job**, because it does not own the terminal and a
-layout that resurrected `&` jobs in the foreground would describe a session nobody
-had. And **an [ephemeral](panes.md#panes-that-were-given-a-command) pane** (the
-editor `C-a e` opened) because restoring one reopens a file somebody finished
-with.
-
-Argv is joined with shell quoting, since `command=` is handed to `/bin/sh -c`:
-`python3 -c 'import x; x.go()'` comes back as one argument rather than three.
-
-What a dump can honestly restore is the _shape_, and the command that made it.
-What it cannot is the state inside a program (a shell's history, a half-written
-commit message, an editor's undo) and it does not pretend otherwise. A pane
-running the session's default shell is dumped as a pane with no command, so
-restoring gives you a fresh one.
-
-Three arguments shape what comes out:
-
-```bash
-slosh -s work cmd '{"cmd":"dump-layout","tab":3}'
-slosh -s work cmd '{"cmd":"dump-layout","relative_to":"~/dev/api","suspend":"commands"}'
-```
-
-| argument | what it says |
-| ------------- | -------------------------------------------------------- |
-| `tab` | a tab **id**, or `0` (the default) for the whole session |
-| `relative_to` | a directory every `cwd=` is written relative to |
-| `suspend` | which panes come back laid out but not running |
-
-`relative_to` is the writing end of the reading rule above: point it at the
-project and the dump comes out with `cwd="."` and `cwd="src"` in it rather than
-your home directory, which is what makes the file committable without an edit
-afterwards. `suspend` takes four words:
-
-| word | means |
-| ---------- | ---------------------------------------------------------------------------------------------------------------- |
-| `as-is` | what is suspended in the session right now, the default here, because a session dump should describe the session |
-| `none` | everything comes back running |
-| `commands` | every pane that was given a command |
-| `all` | nothing starts until you touch it |
-
-`commands` is the one you want for a layout you will open often, and it is what
-[workspaces](workspaces.md) saves with: the pane running this morning's dev server
-belongs in the file asleep, or opening the project starts a dev server every time.
-
-The reply carries the document as `kdl` and counts beside it (`panes` and
-`suspended`) so a caller can report "4 panes, 2 suspended" without parsing back
-the document it was handed. A `tab` that does not exist is an error rather than an
-empty document, because an empty layout is a plausible answer for a tab you closed
-a minute ago and a silent one for a typo in an id.
-
-A dump records `focus=true` in **every** tab, not only the one you were looking
-at, so restoring a six-tab session puts you back where you were in all six.
-
-## Applying one to a running session
-
-```bash
-slosh -s work cmd '{"cmd":"apply-layout","path":"project.layout"}'
-slosh -s work cmd '{"cmd":"apply-layout","kdl":"layout { tab { pane } }","replace":true}'
-```
-
-Without `replace`, the tabs the file describes are added to what is already
-there.
-
-## Moving a pane between tabs
-
-```bash
-slosh cmd '{"cmd":"move-pane","id":3,"tab":2}' # beside that tab's focus
-slosh cmd '{"cmd":"move-pane","id":3,"tab":2,"dir":"rows"}' # under it instead
-slosh cmd '{"cmd":"move-pane","id":3,"tab":0,"name":"logs"}' # into a tab of its own
-```
-
-The pane keeps running: same pty, same scrollback, same process. A move is tree
-surgery, not a new pane and a funeral. The destination is a tab **id** rather than
-an index, because a tab whose last pane leaves is removed and every index after it
-shifts; an id survives that.
-
-Two things a move deliberately drops, both of them the old tab's opinion rather
-than the pane's: a zoom that named it, and its minimised flag. Carried across, the
-first would zoom a pane that has left and the second would file the arrival in a
-strip nobody asked for. Which tab you are _looking_ at does not change.
-
-`C-a >` and `C-a <` push the focused pane one tab along, `C-a b` gives it a tab of
-its own, and a pane can be dragged onto a tab in the strip; see
-[keys](keys.md#defaults) and [the mouse](panes.md#the-mouse).
+A full annotated example is `config/example.layout`.
## Purposes
diff --git a/docs/nav b/docs/nav
index d812a1a..f73a175 100644
--- a/docs/nav
+++ b/docs/nav
@@ -23,6 +23,5 @@ Driving it
scripting.md
Inside
- design.md
windows.md
macos-release.md
diff --git a/docs/workspaces.md b/docs/workspaces.md
index 134b8b2..a685748 100644
--- a/docs/workspaces.md
+++ b/docs/workspaces.md
@@ -1,6 +1,6 @@
# Workspaces
-slosh has the concept of **workspaces**. Essentially a workspace is a
+slosh has the concept of **workspaces**. Essentially a workspace is a
pane layout that represents a project. slosh will scan your config's `project_roots`
folder(s) for two kinds of sub folders: ones that have a `slosh.layout` file, and
ones that have a `.git` but no `slosh.layout`. in the latter case our project picker
@@ -15,89 +15,33 @@ project_roots "~/dev" "~/work" depth=2
project_layout "~/.config/slosh/project.layout"
```
-`project_roots` takes more than one directory because people have more than one
-(work in one tree, everything else in another), and `depth` is how many levels below
-each root to look, 2 by default, 1 to 8.
+`project_roots` takes one or more director{y,ies}, and `depth` is how many levels below
+each root to look, 2 by default.
## From the keyboard
-| key | does |
-|---|---|
-| `w` | the projects picker |
-| `W` | write this tab as this project's layout |
-| `P` | set this pane's purpose |
-
-`w` and `W` are the same shifted pair as `p`/`P`: go to one, write one down. All
-three are bindable as `workspaces`, `save-workspace` and `set-purpose`, and all
-three are in the palette under `projects`, so they work before you have rebound
-anything; see [keys](keys.md#defaults) and [every action](keys.md#every-action).
+| key | action | does |
+| --- | -------------- | --------------------------------------- |
+| `w` | workspaces | the projects picker |
+| `W` | save-workspace | write this tab as this project's layout |
+| `P` | set-purpose | set this pane's purpose |
The picker narrows on a project's name, its path and which marker it has, so typing
`.git` gives you the projects with no layout yet, which is the list you want when
you are about to write one. A project that is already open shows its purpose
instead, and a dot marks the workspace you are in.
-The flow the feature is shaped around, once through:
-
-```
-C-a w picker: ~/work newthing .git · no layout
-Enter your project_layout, cwd bound to newthing
-C-a Enter, npm run dev arrange it, run things
-C-a P service:web Enter tag the pane that matters
-C-a W "wrote slosh.layout · 4 panes, 2 suspended"
-```
-
-Tomorrow, `C-a w Enter` rebuilds that tab with the dev server in the pane you put
-it in and not running. A tab with no name of its own takes the project's, because a
-strip reading `1 2 3` is the thing workspaces are for.
-
-**Closing one is `C-a X`**, which closes the tab and the workspace with it:
-membership *is* the tab's purpose, so there is nothing else to put away and no
-second key to learn ([keys](keys.md#defaults)). `close-workspace` over the socket
-is the same thing for a workspace whose layout opened several tabs: it closes
-every tab carrying that purpose and says how many.
+**Closing one is `C-a X`**, which closes the tab and the workspace with it.
## Saving one
-`C-a W` writes the focused tab to `slosh.layout` in the project's directory,
-and the toast says what happened:
-`wrote slosh.layout · 4 panes, 2 suspended`.
+`C-a W` writes the focused tab to `slosh.layout` in the project's directory.
**What each pane is running goes in with it.** That is what makes writing a layout
the same act as arranging one: split the tab, start the dev server, start the log
tailer, tag the ones that matter, `C-a W`. A pane's terminal has a foreground
process group and that group is the job running in it, so nothing has to be
-declared up front for it to be recorded; see
-[writing one back out](layouts.md#writing-one-back-out) for what is deliberately
-*not* counted as a command (a shell at a prompt, a background job, an ephemeral
-pane).
-
-**A project's layout is not a verbatim dump of the session**, and `suspend` is
-where the two part company. It takes one of four values:
-
-| `suspend` | writes `suspended=true` on |
-|---|---|
-| `as-is` | whatever is suspended right now, the default for `dump-layout` |
-| `none` | nothing |
-| `commands` | every pane that has a command, the default for `save-workspace` |
-| `all` | every pane |
-
-The defaults differ because the two verbs answer different questions.
-[`dump-layout`](layouts.md#writing-one-back-out) is *put this session back*, so it
-reports the state it found. `save-workspace` is *this is what this project needs
-open*, and the pane running this morning's dev server is a pane that should be laid
-out tomorrow rather than started: twelve checked-out projects are not twelve
-running dev servers ([layouts](layouts.md#the-shape)). A suspended pane still shows
-what it would run, so the layout stays readable as a record of the commands.
-
-Neither can restore what is *inside* a program, and the file does not pretend
-otherwise: a shell's history and an editor's buffers are not in it.
-
-**Over the API, writing over a layout that is already there is refused without
-`force`; from the keyboard `C-a W` replaces it and the toast says `replaced`.**
-The asymmetry is deliberate: the operator pressing the key is looking at the
-tab being written, and the file is committed text, so the diff is the
-confirmation. A script has no witness, so it says so in the request.
+declared up front for it to be recorded.
`save-workspace` also takes `path`, for a tab that is not a workspace yet: that is
how a hand-arranged tab becomes a project's layout without opening it through the
@@ -105,12 +49,11 @@ picker first.
`slosh --check slosh.layout` holds the result to the layout schema and
prints one `file:line: what` per problem, so a project's layout goes through the
-same check as everything else you commit; see
-[layouts](layouts.md#what-to-call-them).
+same check as everything else.
## Identity
-A workspace *is* its tab's [purpose](layouts.md#purposes):
+A workspace _is_ its tab's [purpose](layouts.md#purposes):
```
project:api.9f3c1d20
@@ -122,27 +65,7 @@ would have merged them and then focused the wrong one. Membership is that string
and nothing else: no side table of open projects to fall out of step with the tabs.
Because the session declared it, it is **locked**: no program running in a pane
-can relabel a project tab, however chatty its title. That is the same rule any
-declared purpose gets, and it is what makes the purpose safe for a tool to address.
-
-A tab that a layout gave some *other* purpose keeps it and is not a member.
-Honouring it is the point: overwriting a declared purpose is precisely what the
-lock forbids, and a project layout that names a tab `agent:main` meant it.
-`open-workspace` counts those in `honoured`, so a caller can see that a tab was
-built and deliberately not claimed.
-
-## Nothing watches
-
-Discovery is derived, never remembered: one `readdir` per root plus two `stat`s per
-entry, run when the picker opens rather than per frame. **Nothing is stored, so
-nothing can be stale**, and no watcher can promise that. Inotify does not fire for
-a bind mount, an sshfs share or a checkout another machine wrote, so a cache that
-trusted it would be wrong exactly when the answer mattered.
-
-One thing a scan cannot see is a project's layout file changing under a workspace
-that is already open. `workspaces` reports each layout's `mtime`, so a tool that
-keeps the mtime it opened with can compare and notice. Re-applying the new file over
-the running panes is deliberately not offered: it would kill them.
+can relabel a project tab, however chatty its title.
## From another program
@@ -155,12 +78,12 @@ $ slosh -s work cmd '{"cmd":"open-workspace","name":"api"}'
$ slosh -s work cmd '{"cmd":"panes"}' # keep the ones whose tab_id is 3
```
-| verb | takes | answers |
-|---|---|---|
-| `workspaces` | -- | `roots`, and a `workspaces[]` of `name` `path` `purpose` `layout` (the file, or `""`) `mtime` `tab` (0 when it is not open) |
-| `open-workspace` | `name` or `path`; `suspended` | `tab` `purpose` `path` `created` `tabs` `honoured`. Already open, it focuses that tab and answers `created:false` |
-| `close-workspace` | `name` or `purpose` | `closed`, how many tabs went |
-| `save-workspace` | `tab` (0 for the current one), `path` for a tab that is not a workspace yet, `suspend`, `force` | `path` `purpose` `panes` `suspended` `replaced` |
+| verb | takes | answers |
+| ----------------- | ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
+| `workspaces` | -- | `roots`, and a `workspaces[]` of `name` `path` `purpose` `layout` (the file, or `""`) `mtime` `tab` (0 when it is not open) |
+| `open-workspace` | `name` or `path`; `suspended` | `tab` `purpose` `path` `created` `tabs` `honoured`. Already open, it focuses that tab and answers `created:false` |
+| `close-workspace` | `name` or `purpose` | `closed`, how many tabs went |
+| `save-workspace` | `tab` (0 for the current one), `path` for a tab that is not a workspace yet, `suspend`, `force` | `path` `purpose` `panes` `suspended` `replaced` |
Every one of them is in the bare dispatcher too, so `slosh -s work cmd
workspaces` and `slosh -s work cmd open-workspace api` do the same from a shell
@@ -169,12 +92,6 @@ without quoting JSON ([scripting](scripting.md#the-control-socket)).
`open-workspace` is idempotent: asking twice focuses the tab rather than
building a second one, so a script that does not know whether it already ran is
safe. `suspended:true` opens the layout with every
-pane suspended whatever the file said: the *open ten projects, run zero processes*
+pane suspended whatever the file said: the _open ten projects, run zero processes_
case, which is a different question from the one the project answered about which
of its own panes are expensive.
-
-The useful part is the last step. Having opened a workspace, filter
-[`panes`](scripting.md#the-control-socket) by that `tab_id` and act on the purposes
-the project's own layout declared: the project decided that `service:web` is its
-dev server and that it starts suspended, and neither the session nor the program
-driving it had to know that beforehand.