From 586e4ea222b8edccc42f42d5f49fcb4f3352b19f Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Mon, 9 Mar 2026 15:37:19 -0400 Subject: [PATCH 1/6] feat: add session_child_toggle key command Add a toggle command to switch between primary and child sessions: - Ctrl+X+Down (default) toggles between views - When in primary session: goes to first child - When in child session: goes to parent This provides a single key to navigate between session hierarchies instead of needing separate keys for child_first and parent navigation. --- .../src/cli/cmd/tui/routes/session/index.tsx | 27 +++++++++++++++++++ packages/opencode/src/config/config.ts | 1 + 2 files changed, 28 insertions(+) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index d3a4ff81e01..0ae7121893c 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -342,6 +342,22 @@ export function Session() { } } + function toggleChildSession() { + if (session()?.parentID) { + // Currently in child, go to parent + const parentID = session()?.parentID + if (parentID) { + navigate({ + type: "session", + sessionID: parentID, + }) + } + } else { + // Currently in primary, go to first child + moveFirstChild() + } + } + function childSessionHandler(func: (dialog: DialogContext) => void) { return (dialog: DialogContext) => { if (!session()?.parentID || dialog.stack.length > 0) return @@ -936,6 +952,17 @@ export function Session() { dialog.clear() }), }, + { + title: "Toggle child session", + value: "session.child.toggle", + keybind: "session_child_toggle", + category: "Session", + hidden: true, + onSelect: (dialog) => { + toggleChildSession() + dialog.clear() + }, + }, { title: "Next child session", value: "session.child.next", diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 6b4242a225a..eb28e1af781 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -901,6 +901,7 @@ export namespace Config { session_child_cycle: z.string().optional().default("right").describe("Go to next child session"), session_child_cycle_reverse: z.string().optional().default("left").describe("Go to previous child session"), session_parent: z.string().optional().default("up").describe("Go to parent session"), + session_child_toggle: z.string().optional().default("down").describe("Toggle between primary and child sessions"), terminal_suspend: z.string().optional().default("ctrl+z").describe("Suspend terminal"), terminal_title_toggle: z.string().optional().default("none").describe("Toggle terminal title"), tips_toggle: z.string().optional().default("h").describe("Toggle tips on home screen"), From aa286a2bb4a6c234999a165b082ec839eab827ce Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Mon, 9 Mar 2026 15:42:34 -0400 Subject: [PATCH 2/6] feat: make session_child_toggle unbound by default Change default binding from 'down' to 'none' so users must explicitly configure the key if they want to use this feature. --- packages/opencode/src/config/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index eb28e1af781..8fa26f13508 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -901,7 +901,7 @@ export namespace Config { session_child_cycle: z.string().optional().default("right").describe("Go to next child session"), session_child_cycle_reverse: z.string().optional().default("left").describe("Go to previous child session"), session_parent: z.string().optional().default("up").describe("Go to parent session"), - session_child_toggle: z.string().optional().default("down").describe("Toggle between primary and child sessions"), + session_child_toggle: z.string().optional().default("none").describe("Toggle between primary and child sessions"), terminal_suspend: z.string().optional().default("ctrl+z").describe("Suspend terminal"), terminal_title_toggle: z.string().optional().default("none").describe("Toggle terminal title"), tips_toggle: z.string().optional().default("h").describe("Toggle tips on home screen"), From 79c7e3383667291c579024d281103c8a1efc5c77 Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Mon, 9 Mar 2026 15:56:37 -0400 Subject: [PATCH 3/6] fix: add session_child_toggle keybind handler Add the keyboard handler for session_child_toggle that was missing. The toggle now properly responds to the configured keybind in tui.json. Handler is placed before the parentID guard so it works from both primary and child sessions. --- packages/opencode/src/cli/cmd/tui/routes/session/index.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 0ae7121893c..9db6dabbba5 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -254,6 +254,10 @@ export function Session() { }) useKeyboard((evt) => { + if (keybind.match("session_child_toggle", evt)) { + toggleChildSession() + return + } if (!session()?.parentID) return if (keybind.match("app_exit", evt)) { exit() From db52dd456461c7d481a82ebc7ba84afcd393673f Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Mon, 9 Mar 2026 16:19:29 -0400 Subject: [PATCH 4/6] fix: remove redundant useKeyboard handler for session_child_toggle The redundant hook was interfering with the command dialog handler. The toggle now relies solely on the command dialog system. --- packages/opencode/src/cli/cmd/tui/routes/session/index.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 9db6dabbba5..0ae7121893c 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -254,10 +254,6 @@ export function Session() { }) useKeyboard((evt) => { - if (keybind.match("session_child_toggle", evt)) { - toggleChildSession() - return - } if (!session()?.parentID) return if (keybind.match("app_exit", evt)) { exit() From 9bef0b434ad9cfa6a4a8013fd18ea927e92bc1e5 Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Mon, 9 Mar 2026 18:50:02 -0400 Subject: [PATCH 5/6] feat: add dynamic title to session_child_toggle command Title changes based on current session state: - In primary session: 'Show child sessions' - In child session: 'Return to parent session' --- packages/opencode/src/cli/cmd/tui/routes/session/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 0ae7121893c..b6b6a701636 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -953,7 +953,7 @@ export function Session() { }), }, { - title: "Toggle child session", + title: session()?.parentID ? "Return to parent session" : "Show child sessions", value: "session.child.toggle", keybind: "session_child_toggle", category: "Session", From 0af98f5a0ac964ce0cbaf35ba2630487a5f5ad3a Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Mon, 9 Mar 2026 18:56:07 -0400 Subject: [PATCH 6/6] fix: remove hidden flag from session_child_toggle command The command was marked as hidden, which prevented it from appearing in the command palette. Now it will be visible with its dynamic title that changes based on session state. --- packages/opencode/src/cli/cmd/tui/routes/session/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index b6b6a701636..44a8173eb7d 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -957,7 +957,6 @@ export function Session() { value: "session.child.toggle", keybind: "session_child_toggle", category: "Session", - hidden: true, onSelect: (dialog) => { toggleChildSession() dialog.clear()