aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authoruser <user@clank>2026-09-29 14:26:43 +0200
committeruser <user@clank>2026-09-29 14:26:43 +0200
commite67131c52f91b01758cd56359848fbb11e303958 (patch)
treeaf9d7760d77109ab28884a1a8a65da8784743155 /docs
parentlayouts, moves: build panes without pulling the view to them (diff)
downloadslosh-e67131c52f91b01758cd56359848fbb11e303958.tar.gz
slosh-e67131c52f91b01758cd56359848fbb11e303958.tar.xz
slosh-e67131c52f91b01758cd56359848fbb11e303958.zip
docs: the pane you are in, the pane you name, and not moving the view
$SLOSH_PANE in the man page's environment list, next to SLOSH and SLOSH_SESSION, and the scripting guide grows the section that follows from it: read yourself with `capture`, build with `apply-layout focus:false`, place with `move-pane beside`, and never read `focused` as "where do I live". `pid` is written down as the answer for anything that cannot read its own environment. (Three roff \(em escapes had got into the markdown table; they are em dashes like the rest of the file.)
Diffstat (limited to 'docs')
-rw-r--r--docs/scripting.md47
-rw-r--r--docs/slosh.125
2 files changed, 68 insertions, 4 deletions
diff --git a/docs/scripting.md b/docs/scripting.md
index 8fff400..74c36d4 100644
--- a/docs/scripting.md
+++ b/docs/scripting.md
@@ -20,23 +20,24 @@ Panes and tabs are addressed by **id**, so a background tab is scriptable.
| verb | |
|---|---|
-| `panes` `tabs` | what exists, with ids, rects, titles, purposes, state. A pane's `tab_id` is what `move-pane` and `select-tab` want; its `tab` is where that tab sits in the strip |
+| `panes` `tabs` | what exists, with ids, rects, titles, purposes, state. A pane's `tab_id` is what `move-pane` and `select-tab` want; its `tab` is where that tab sits in the strip; its `pid` is the program in it |
| `snapshot` | the composited screen, as JSON, `format:"text"`, or `format:"bytes"`, the emitter's own output for this frame (a second call is the delta) |
+| `capture` | one pane's visible text: `id` (0 = focused). Reads the pane's own terminal, so it works for a pane in a tab that is not on screen, and changes nothing — no `select-tab`, no focus, no effect on a selection |
| `deadline` | when the session wants its next frame, in ms, or -1 |
| `send` | bytes as if typed, decoded like input (`"data":"\\x01\\\\"`) |
-| `raw` | bytes straight into the focused pane's pty |
+| `raw` | bytes straight into a pane's pty: `id` (0 = focused). Naming the pane is the point — focusing one first moves the view of whoever is watching |
| `resize` | `cols` `rows`, and optionally `cell_w` `cell_h` |
| `split` | `dir:"cols"\|"rows"`, `id` for which pane to split |
| `focus` | `id` |
| `close` `rerun` | `id`, or 0 for the focused pane |
| `clear-shaders` | `id`, or 0 for the focused pane; answers `cleared:0\|1`. The way back from a pane that painted itself unreadable |
| `new-tab` `select-tab` `close-tab` `move-tab` | tabs, by `id` or `index` |
-| `move-pane` | `id` of the pane, `tab` id to move it into (`0` for a tab of its own, with an optional `name`), `dir:"cols"\|"rows"`. The pane keeps running: same pty, same scrollback |
+| `move-pane` | `id` of the pane, `tab` id to move it into (`0` for a tab of its own, with an optional `name`), `dir:"cols"\|"rows"`, `beside` a pane in that tab to land next to (default: whatever it has focused), `focus:false` to leave focus — and the view — where it is. The pane keeps running: same pty, same scrollback |
| `float` | bare: toggle a pane [floating](panes.md#floating-a-pane) (`id`, or 0 for the focused one). With any of `x` `y` `w` `h` it *places*: floats first when tiled, re-places when floating, never un-floats; omitted fields keep their value |
| `new-float` | a fresh floating shell over the current tab, centred, in the focused pane's directory; answers `id` |
| `set-name` | `target:"tab"` (the default, because that is what this verb has always meant) or `"pane"`, `id`, `name`. A pane accepts 0 for the focused one. A pane's name wins over the title the program sets, so this is how a program that keeps announcing something stale gets overruled; an empty `name` clears it and hands the label back. Refusals say `no such pane` or `no such tab`, so a mistyped target is visible in the reply |
| `set-purpose` | `target:"pane"\|"tab"`, `id` (or 0 for the focused pane, and the tab you are in), `purpose`. An empty `purpose` clears the slot *and* unlocks it, handing the label back to the program |
-| `dump-layout` `apply-layout` | see [layouts](layouts.md). `dump-layout` takes `tab` (0 for every tab), `relative_to` to write every `cwd=` under that directory instead of absolute, and `suspend` (`as-is` `none` `commands` `all`); it answers `kdl` `panes` `suspended`, and an unknown `tab` is an error rather than an empty document |
+| `dump-layout` `apply-layout` | see [layouts](layouts.md). `apply-layout` takes `focus:false` to build the tabs without going to them (a layout that names an `active` tab still wins); the panes it builds are sized either way. `dump-layout` takes `tab` (0 for every tab), `relative_to` to write every `cwd=` under that directory instead of absolute, and `suspend` (`as-is` `none` `commands` `all`); it answers `kdl` `panes` `suspended`, and an unknown `tab` is an error rather than an empty document |
| `workspaces` | the projects on disk and which of them are open: `roots` says whether any are configured at all, and each entry has `name` `path` `purpose` `layout` (a file path, or "") `mtime` `tab` (0 when closed). See [workspaces](workspaces.md) |
| `open-workspace` | `name` or `path`, and `suspended`; answers `tab` `purpose` `path` `created` `tabs` `honoured`. Already open means focused, with `created:false` |
| `close-workspace` | `name` or `purpose`; answers `closed`, how many tabs went |
@@ -51,6 +52,44 @@ Panes and tabs are addressed by **id**, so a background tab is scriptable.
| `alive` | is it running, and how many panes and tabs |
| `quit` | end the session |
+## Your own pane, not the focused one
+
+A program in a pane is told which pane it is in: **`$SLOSH_PANE`**. Everything
+else follows from having it.
+
+```bash
+$S '{"cmd":"capture","id":'$SLOSH_PANE'}' # read yourself
+$S '{"cmd":"panes"}' | jq --argjson me $SLOSH_PANE '.panes[] | select(.id==$me) | .tab_id'
+```
+
+**Do not use `focused` for this.** Exactly one pane in the session is focused,
+it is in the tab currently on screen, and it belongs to whoever is looking —
+which is not you. A tool that read focus as "where I live" put its panes in
+whatever tab the human had wandered into, and in a session with several such
+tools running it was reliably somebody else's. `focused` answers "what is the
+human doing"; `$SLOSH_PANE` answers "who am I".
+
+So background work is three calls, none of which move anybody's view:
+
+```bash
+my_tab=$($S '{"cmd":"panes"}' | jq --argjson me $SLOSH_PANE '.panes[]|select(.id==$me)|.tab_id')
+
+# 1. build a pane that was *given* a command, without going to it
+$S '{"cmd":"apply-layout","focus":false,"kdl":"layout { tab name=\"work\" { pane purpose=\"task:build\" command=\"make -j8\" } }"}'
+id=$($S '{"cmd":"panes"}' | jq '.panes[]|select(.purpose=="task:build")|.id')
+
+# 2. move it in beside yourself, still without taking focus
+$S "{\"cmd\":\"move-pane\",\"id\":$id,\"tab\":$my_tab,\"beside\":$SLOSH_PANE,\"focus\":false}"
+
+# 3. read it wherever it is
+$S "{\"cmd\":\"capture\",\"id\":$id}"
+```
+
+If you cannot read the environment — a scrubbed env, a helper with no
+controlling tty, a process several forks down — `panes` reports each pane's
+**`pid`**, so walking your own parents until one of them matches finds your pane
+without asking anybody about focus.
+
## Driving a project
Everything a program needs in a project it has never seen: open it, ask what is
diff --git a/docs/slosh.1 b/docs/slosh.1
index aba19ac..2d792fa 100644
--- a/docs/slosh.1
+++ b/docs/slosh.1
@@ -397,6 +397,31 @@ and everything still works.
.B 1
: you are inside a pane.
.TP
+.B SLOSH_PANE
+Which pane this is: the id
+.B panes
+reports, and the one every verb that takes an
+.B id
+wants.
+A program has to be able to name the pane it is
+.IR in :
+.B panes
+says which pane is
+.IR focused ,
+and focus belongs to whoever is looking, so a tool that inferred "where do
+I live" from it put its work in whichever tab somebody else was in.
+The id is the pane's, not the process's \(en a
+.B rerun
+keeps it.
+See
+.B TMUX_PANE
+and
+.B ZELLIJ_PANE_ID
+for the same idea elsewhere.
+Unset under
+.BR \-\-script 's
+own stdio, and for anything not in a pane.
+.TP
.B SLOSH_SESSION
The name of the session this pane belongs to: which one of possibly
several, the part a program cannot work out for itself.