diff --git a/.config/opencode/plugins/tmux-panes.ts b/.config/opencode/plugins/tmux-panes.ts index 72c89fa..9fe12a7 100644 --- a/.config/opencode/plugins/tmux-panes.ts +++ b/.config/opencode/plugins/tmux-panes.ts @@ -6,7 +6,11 @@ import { spawn } from "bun" * * When opencode spawns a background subagent, this plugin automatically opens * a new tmux pane showing that subagent's live TUI via `opencode attach`. - * The pane closes when the subagent session ends. + * + * Layout: + * - First subagent: horizontal 60/40 split — main pane on left, subagent on right + * - Additional subagents: stacked vertically in the right column + * - Panes close automatically when subagent sessions end * * Only activates when running inside a tmux session (TMUX env var is set). */ @@ -21,6 +25,10 @@ const plugin: Plugin = async (ctx) => { const sourcePaneId = getCurrentPaneId() const serverUrl = ctx.serverUrl.toString() + // Ordered list of pane IDs in the right column. + // Empty = no right column yet; length > 0 = right column exists. + const rightColumnPanes: string[] = [] + return { event: async ({ event }) => { // Spawn a new pane when a subagent session is created @@ -32,24 +40,45 @@ const plugin: Plugin = async (ctx) => { if (sessions.has(sessionId)) return const cmd = `opencode attach ${serverUrl} --session ${sessionId}` - const proc = spawn( - [ + + let args: string[] + if (rightColumnPanes.length === 0) { + // First subagent: open a horizontal split, giving the new (right) + // pane 40% of the window width so the main pane keeps 60%. + args = [ "tmux", "split-window", - "-h", // horizontal split + "-h", // horizontal split — new pane appears on the right + "-p", "40", // new pane gets 40% of window width "-d", // don't focus the new pane "-P", "-F", - "#{pane_id}", // print the new pane's ID + "#{pane_id}", ...(sourcePaneId ? ["-t", sourcePaneId] : []), cmd, - ], - { stdout: "pipe", stderr: "pipe" }, - ) + ] + } else { + // Additional subagents: stack vertically within the right column + // by splitting the last right-column pane horizontally. + const lastRightPane = rightColumnPanes[rightColumnPanes.length - 1] + args = [ + "tmux", + "split-window", + "-v", // vertical split — new pane stacks below the target + "-d", // don't focus the new pane + "-P", + "-F", + "#{pane_id}", + "-t", lastRightPane, + cmd, + ] + } + const proc = spawn(args, { stdout: "pipe", stderr: "pipe" }) const paneId = (await new Response(proc.stdout).text()).trim() if ((await proc.exited) === 0 && paneId) { sessions.set(sessionId, paneId) + rightColumnPanes.push(paneId) } } @@ -63,6 +92,8 @@ const plugin: Plugin = async (ctx) => { stderr: "ignore", }) sessions.delete(info.id) + const idx = rightColumnPanes.indexOf(paneId) + if (idx !== -1) rightColumnPanes.splice(idx, 1) } } },