diff options
| author | sl0p <sl0p@sl0p.foo> | 2026-08-13 22:58:24 +0200 |
|---|---|---|
| committer | sl0p <sl0p@sl0p.foo> | 2026-08-13 22:58:24 +0200 |
| commit | 7d416d95cb49f783f8c4ee6eddd5c2cdcd81bb40 (patch) | |
| tree | 6aaced8bd6927565816ce48ae07fb5f397d6edf7 /src/app.h | |
| parent | sl0ptty: M2 — server/client split, detach/reattach, control socket (diff) | |
| download | slosh-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.h | 26 |
1 files changed, 25 insertions, 1 deletions
@@ -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 */ |
