aboutsummaryrefslogtreecommitdiffstats
path: root/src/cmd.c
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-16 19:13:28 +0200
committeruser <user@clank>2026-08-16 19:13:28 +0200
commitc58d873b409c7a166a64ceabf178692b0b4467c2 (patch)
tree45ed8190996bfa1c7b59309c1b07d50f5dadae19 /src/cmd.c
parentsl0ppty: the prompt speaks config, not a dialect of it (diff)
downloadslosh-c58d873b409c7a166a64ceabf178692b0b4467c2.tar.gz
slosh-c58d873b409c7a166a64ceabf178692b0b4467c2.tar.xz
slosh-c58d873b409c7a166a64ceabf178692b0b4467c2.zip
sl0ppty: a pane can change tabs
sl0ppty cmd '{"cmd":"move-pane","id":3,"tab":2}' sl0ppty cmd '{"cmd":"move-pane","id":3,"tab":2,"dir":"rows"}' sl0ppty cmd '{"cmd":"move-pane","id":3,"tab":0,"name":"logs"}' The mechanism only. There is no key and no gesture for it yet -- that is the next conversation, and guessing at it now would mean building the wrong one twice. **The pane keeps running.** Measured, not assumed: `pid=3062557` before the move and `pid=3062557` after, in a different tab, with its scrollback intact and still answering what it is sent. That is the whole reason to move a pane rather than open one where you wanted it and close this one. It is tree surgery and it reuses the surgery that was already here. `place_beside` is the second half of `split_node_with` -- everything after "make the leaf" -- lifted out so that a leaf which already exists somewhere else can be grafted the same way, flat sibling or new split, on either side. It takes the tab rather than `cur(a)` now, because replacing a root has to land on the right one and `cur(a)` was only right while the sole caller was splitting the pane you were looking at. `detach_leaf` is `close_leaf` without the funeral. Three decisions worth naming: **The destination is a tab id, not an index.** A tab whose last pane leaves has nothing to be, so it is removed -- and every index after it moves. An id survives that; an index quietly means a different tab. The move re-finds the destination by id *after* the removal for exactly this reason, and there is a test that empties a tab and checks the pane landed where it was sent. **A move drops what the old tab thought about the pane** -- a zoom naming it, and its minimised flag. Both are the tab's opinion and not the pane's: carried across, the first zooms a pane that has left and the second files the arrival in a strip nobody asked for. **It does not follow the pane.** Which tab you are looking at is a question about intent, and this is the mechanism; `move-pane` answers with the tab id so a caller can select it if that is what it meant. "Into a tab of its own" is its own call rather than new-tab-then-move, because a pane that is the only thing in its tab would otherwise have its tab removed from under the new one, and the order those two happen in is precisely the bug. Verified: `tests/test_move_pane.py`, 25 checks -- the pane still running and still talking afterwards, placement beside and under the destination's focus, focus landing on the arrival, the emptied tab, a tab of its own, every refusal (no such pane, no such tab, already there, already alone), the dropped zoom and minimise, and after three moves a layout with no overlaps that dumps and rebuilds as itself.
Diffstat (limited to 'src/cmd.c')
-rw-r--r--src/cmd.c15
1 files changed, 15 insertions, 0 deletions
diff --git a/src/cmd.c b/src/cmd.c
index 556b32b..a8af4a7 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -176,6 +176,21 @@ static char *cmd_json(app_t *a, screen_t *s, input_parser_t *in,
bool had = app_clear_pane_shaders(a, (uint32_t)jv_geti(req, "id", 0));
return jok_int("cleared", had ? 1 : 0);
}
+ /* `tab` is an id, `0` a tab of its own -- the same "0 means the obvious thing"
+ * the pane arguments use. `dir` decides beside or under, like `split`. */
+ if (strcmp(cmd, "move-pane") == 0) {
+ uint32_t pid = (uint32_t)jv_geti(req, "id", 0);
+ uint32_t tid = (uint32_t)jv_geti(req, "tab", 0);
+ bool rows = strcmp(jv_gets(req, "dir", "cols"), "rows") == 0;
+ if (!tid) {
+ uint32_t made = app_move_pane_to_new_tab(a, pid, jv_gets(req, "name", ""));
+ if (!made) return jerr("cannot move it to a tab of its own");
+ return jok_int("tab", (long long)made);
+ }
+ if (!app_move_pane_to_tab(a, pid, tid, rows))
+ return jerr("cannot move it there");
+ return jok_int("tab", (long long)tid);
+ }
if (strcmp(cmd, "new-tab") == 0) {
uint32_t id = app_new_tab(a, jv_gets(req, "name", ""));
if (!id) return jerr("cannot create tab");