aboutsummaryrefslogtreecommitdiffstats
path: root/src/app.h
diff options
context:
space:
mode:
authorsl0p <sl0p@sl0p.foo>2026-08-13 22:58:24 +0200
committersl0p <sl0p@sl0p.foo>2026-08-13 22:58:24 +0200
commit7d416d95cb49f783f8c4ee6eddd5c2cdcd81bb40 (patch)
tree6aaced8bd6927565816ce48ae07fb5f397d6edf7 /src/app.h
parentsl0ptty: M2 — server/client split, detach/reattach, control socket (diff)
downloadslosh-7d416d95cb49f783f8c4ee6eddd5c2cdcd81bb40.tar.gz
slosh-7d416d95cb49f783f8c4ee6eddd5c2cdcd81bb40.tar.xz
slosh-7d416d95cb49f783f8c4ee6eddd5c2cdcd81bb40.zip
sl0ptty: M3 — tabs, purposes, and the JSON control API
Tabs are layout trees, one per tab, and panes in every tab keep running: fds, pumping and reaping span all of them, only the current one is composed. A one-row strip shows them (M5 makes it the real status bar) and each label registers a hit, so clicking a tab switches to it through the same mechanism as every other click. C-a c new tab C-a n / C-a p cycle C-a 1..9 select Purposes on panes and tabs with D8's trust model implemented as a lock: declared (from a layout or an operator) outranks in-band, cannot be overridden, and everything is sanitised to [A-Za-z0-9_.:/-] on ingest — 'project:foo bar/../$(rm -rf)' becomes 'project:foobar/../rm-rf'. The in-band path arrives with the OSC scanner in M4 and always passes declared:false; the refusal is already tested. The control API is now JSON in, JSON out: {"cmd":"new-tab","name":"agent","purpose":"project:x.deadbeef"} {"ok":true,"id":2} with panes/tabs/snapshot/send/raw/resize/split/focus/close/select-tab/ set-name/set-purpose/alive/quit. Panes are addressed by id, not by 'the focused one', so a background tab can be scripted. The bare-verb form stays as a harness alias and runs the same code, so the tests cannot drift from the API. Adds a small JSON reader (jsonval.c) — we could only emit until now. 129 checks green.
Diffstat (limited to 'src/app.h')
-rw-r--r--src/app.h26
1 files changed, 25 insertions, 1 deletions
diff --git a/src/app.h b/src/app.h
index f9d8b58..9d38cba 100644
--- a/src/app.h
+++ b/src/app.h
@@ -38,7 +38,31 @@ bool app_detach_requested(const app_t *a);
void app_clear_detach(app_t *a);
size_t app_pane_count(const app_t *a);
-/* Layout as JSON, for the harness: id, rect, focus, title. Caller frees. */
+/* Tabs. A tab is a layout tree; panes in every tab keep running. */
+uint32_t app_new_tab(app_t *a, const char *name);
+bool app_select_tab(app_t *a, size_t index);
+bool app_select_tab_id(app_t *a, uint32_t id);
+void app_cycle_tab(app_t *a, int delta);
+size_t app_tab_count(const app_t *a);
+bool app_set_tab_name(app_t *a, uint32_t id, const char *name);
+
+/* Purposes (D8). `declared` means "from a layout/control API": it outranks an
+ * in-band purpose and locks it, so a pane cannot relabel itself afterwards.
+ * Returns false when a locked purpose refuses an in-band change. */
+bool app_set_pane_purpose(app_t *a, uint32_t id, const char *purpose,
+ bool declared);
+bool app_set_tab_purpose(app_t *a, uint32_t id, const char *purpose,
+ bool declared);
+
+/* Address panes by id (the control API), not "the focused one". */
+bool app_focus_pane(app_t *a, uint32_t id);
+bool app_split_pane(app_t *a, uint32_t id, bool rows);
+bool app_close_pane(app_t *a, uint32_t id);
+uint32_t app_focused_pane_id(app_t *a);
+uint32_t app_current_tab_id(app_t *a);
+
+/* State as JSON, for the control API and the harness. Caller frees. */
char *app_panes_json(app_t *a);
+char *app_tabs_json(app_t *a);
#endif /* SL0PTTY_APP_H */