aboutsummaryrefslogtreecommitdiffstats
path: root/cli/src
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-13 22:07:45 +0200
committeruser <user@clank>2026-08-13 22:07:45 +0200
commita8ea4e888938e562885b1a0f009f5743939a879b (patch)
tree2ae5df21d4919cfe128830376b55dca814418496 /cli/src
parentchecks: ignore __pycache__ (the up check imports a sibling check) (diff)
downloadsl0ppi-a8ea4e888938e562885b1a0f009f5743939a879b.tar.gz
sl0ppi-a8ea4e888938e562885b1a0f009f5743939a879b.tar.xz
sl0ppi-a8ea4e888938e562885b1a0f009f5743939a879b.zip
sl0p: keep a templated tab's purpose, so runtime tabs get a status bar
Clicking the bar's `+` made a tab with no status bar. The button only exposed it: pressing `n` in TAB mode did the same, in any `sl0ppi up` session. The chain: a generated layout had no default_tab_template, so a tab created at runtime had nothing to take a bar from. It had none because parse_tab_node_with_template read a tab node's name, cwd and focus but never its `purpose` -- so templating erased the purpose that IS identity here (`sl0ppi ls` finds project tabs by it), and the CLI worked around that by inlining the template into every tab instead of emitting it. Fixed at the root: a templated tab keeps its own purpose, and the CLI emits `default_tab_template` again. The generated layout also shrinks -- one copy of the bar's configuration instead of one per tab. The two properties trade off against each other, so sl0ppi-up-live.py now asserts both at once: `sl0ppi ls` still sees a tab per project, and every tab including one created at runtime has exactly one status bar. Confirmed by reverting each half separately: without the parser fix the tabs lose their purpose and `ls` shows "alpha —"; without the CLI change the runtime tab has zero bars, which is the reported bug. Two measurement traps cost an hour here and are now written down: counting bars by grepping dump-layout also counts the template and every swap layout ("4 bars for 3 tabs"), and list-panes stops reporting borderless plugin panes for a tab once a client has rendered it. Also in here, from the same session: - the split button check found buttons by searching for " + " on any row with a frame glyph, which matches the stack list's "+N more" once two panes sit side by side, and then reports the product broken. It now requires the frame corner the button is drawn against. - the checks killed their sessions but did not delete them, so each run left an "EXITED - attach to resurrect" entry: 35 had quietly filled the session picker. They now delete-session by name. - and the trap that cost the user their running client: I killed a bare `sl0ppij` because "there are no user sessions on this box". That was a guess, not evidence, and wrong. A check's processes always carry that run's own socket dir or `attach sl0ptest-...`; a bare `sl0ppij` with a cwd in the repo is a human's. Written into the handover next to the `pkill -f` trap, which is the same mistake in a different costume.
Diffstat (limited to 'cli/src')
-rw-r--r--cli/src/core/layout.ts44
1 files changed, 30 insertions, 14 deletions
diff --git a/cli/src/core/layout.ts b/cli/src/core/layout.ts
index 6c2319e..0aa8e5c 100644
--- a/cli/src/core/layout.ts
+++ b/cli/src/core/layout.ts
@@ -161,16 +161,8 @@ function indent(text: string, spaces: number): string {
/**
* Fold a tab template around a project's panes.
*
- * We do NOT emit `default_tab_template` into the generated layout, even though
- * that is what it is for: a templated tab loses its `purpose=`, because the
- * fork hangs a tab's purpose on the tab's root layout and templating replaces
- * it. Verified -- with a template every tab came back `purpose=None`, which
- * breaks identity outright: `sl0ppi ls` sees no project tabs and `up` would
- * cheerfully create a second set.
- *
- * So we inline instead: take the template body and substitute the project's
- * panes for its `children` node. The tab node keeps its own name/purpose/cwd,
- * and the status bar comes along.
+ * Kept for callers that hand a layout STRING to a running session. The session
+ * layout emits `default_tab_template` instead -- see `sessionLayoutKdl`.
*/
export function applyTemplate(fragment: string, template: string): string | undefined {
const body = template.slice(template.indexOf("{") + 1, template.lastIndexOf("}"));
@@ -219,17 +211,41 @@ function preamble(paneTemplates?: string[]): string {
return `${paneTemplates.map((template) => indent(template, 4)).join("\n")}\n`;
}
-/** A whole-session layout: what `up` passes to `--layout` when creating. */
+/**
+ * A whole-session layout: what `up` passes to `--layout` when creating.
+ *
+ * The tab template is EMITTED, not inlined into each tab. It used to be
+ * inlined, because a templated tab came back with `purpose=None` and purpose is
+ * identity here -- `sl0ppi ls` finds project tabs by it. The fork now keeps a
+ * tab's own purpose when a template is applied
+ * (`sl0p_a_templated_tab_keeps_its_own_purpose`), which removes that reason and
+ * fixes the thing the workaround cost us: a tab created at RUNTIME -- `new-tab`
+ * from a keybinding, or the bar's `+` button -- had no template to take a
+ * status bar from, and came up bare.
+ */
export function sessionLayoutKdl(
specs: TabSpec[],
template?: string,
paneTemplates?: string[],
): string {
- const tabs = specs.map((spec) => tabKdl(spec, 4, template)).join("\n");
- return `layout {\n${preamble(paneTemplates)}${tabs}\n}\n`;
+ const tabs = specs.map((spec) => tabKdl(spec, 4)).join("\n");
+ return `layout {\n${preamble(paneTemplates)}${templateBlock(template)}${tabs}\n}\n`;
}
-/** A one-tab layout: what `up` passes to `new-tab --layout-string`. */
+/** The `default_tab_template { ... }` node, indented into the layout block. */
+function templateBlock(template?: string): string {
+ if (!template) return "";
+ return `${indent(template, 4)}\n`;
+}
+
+/**
+ * A one-tab layout: what `open` passes to `new-tab --layout-string`.
+ *
+ * Still inlined: this layout describes ONE tab being added to a session that
+ * already exists, and a `default_tab_template` in it would apply to that tab
+ * anyway -- but the session's own template (if it has one) would apply first,
+ * and the tab would come up with two status bars.
+ */
export function singleTabLayoutKdl(
spec: TabSpec,
template?: string,