From 87aaa76c61a0cfbb8de1a4e8b0c79ca54b6ae057 Mon Sep 17 00:00:00 2001 From: huangli Date: Thu, 16 Apr 2026 14:55:18 +0800 Subject: [PATCH 1/2] fix(tui): allow leader-down to navigate into grandchild sessions from any level --- .../src/cli/cmd/tui/routes/session/index.tsx | 11 +++++---- .../tui/routes/session/subagent-footer.tsx | 23 +++++++++++++++---- 2 files changed, 25 insertions(+), 9 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 c04e58acecae..dcceb135ff04 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -353,8 +353,9 @@ export function Session() { const local = useLocal() function moveFirstChild() { - if (children().length === 1) return - const next = children().find((x) => !!x.parentID) + const currentID = session()?.id + if (!currentID) return + const next = sync.data.session.find((s) => s.parentID === currentID) if (next) { navigate({ type: "session", @@ -364,9 +365,11 @@ export function Session() { } function moveChild(direction: number) { - if (children().length === 1) return + const currentParentID = session()?.parentID + if (!currentParentID) return - const sessions = children().filter((x) => !!x.parentID) + const sessions = children().filter((x) => x.parentID === currentParentID) + if (sessions.length <= 1) return let next = sessions.findIndex((x) => x.id === session()?.id) - direction if (next >= sessions.length) next = 0 diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/subagent-footer.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/subagent-footer.tsx index 55695996076e..3b7274a01ca4 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/subagent-footer.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/subagent-footer.tsx @@ -17,18 +17,19 @@ export function SubagentFooter() { const subagentInfo = createMemo(() => { const s = session() - if (!s) return { label: "Subagent", index: 0, total: 0 } + if (!s) return { label: "Subagent", index: 0, total: 0, hasChildren: false } const agentMatch = s.title.match(/@(\w+) subagent/) const label = agentMatch ? Locale.titlecase(agentMatch[1]) : "Subagent" - if (!s.parentID) return { label, index: 0, total: 0 } + if (!s.parentID) return { label, index: 0, total: 0, hasChildren: false } const siblings = sync.data.session .filter((x) => x.parentID === s.parentID) .toSorted((a, b) => a.time.created - b.time.created) const index = siblings.findIndex((x) => x.id === s.id) + const hasChildren = sync.data.session.some((x) => x.parentID === s.id) - return { label, index: index + 1, total: siblings.length } + return { label, index: index + 1, total: siblings.length, hasChildren } }) const usage = createMemo(() => { @@ -58,8 +59,8 @@ export function SubagentFooter() { const { theme } = useTheme() const keybind = useKeybind() const command = useCommandDialog() - const [hover, setHover] = createSignal<"parent" | "prev" | "next" | null>(null) - useTerminalDimensions() + const [hover, setHover] = createSignal<"parent" | "prev" | "next" | "child" | null>(null) + const dimensions = useTerminalDimensions() return ( @@ -123,6 +124,18 @@ export function SubagentFooter() { Next {keybind.print("session_child_cycle")} + + setHover("child")} + onMouseOut={() => setHover(null)} + onMouseUp={() => command.trigger("session.child.first")} + backgroundColor={hover() === "child" ? theme.backgroundElement : theme.backgroundPanel} + > + + Child {keybind.print("session_child_first")} + + + From 2e574ac9c3eed591fa5e262e91b116e24365c0a2 Mon Sep 17 00:00:00 2001 From: huangli Date: Fri, 17 Apr 2026 15:08:57 +0800 Subject: [PATCH 2/2] fix(tui): use bare down key to enter child session in subagent view Match the pattern of up/left/right navigation in subagent view by using a bare down key instead of down to navigate into child sessions. Only the root session view still requires down. --- .../src/cli/cmd/tui/routes/session/index.tsx | 13 +++++++++++++ .../cli/cmd/tui/routes/session/subagent-footer.tsx | 4 ++-- packages/opencode/src/config/keybinds.ts | 1 + 3 files changed, 16 insertions(+), 2 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 dcceb135ff04..618601e6285b 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -962,11 +962,24 @@ export function Session() { keybind: "session_child_first", category: "Session", hidden: true, + enabled: !session()?.parentID, onSelect: (dialog) => { moveFirstChild() dialog.clear() }, }, + { + title: "Go to child session", + value: "session.child.first.subagent", + keybind: "session_child_first_subagent", + category: "Session", + hidden: true, + enabled: !!session()?.parentID, + onSelect: childSessionHandler((dialog) => { + moveFirstChild() + dialog.clear() + }), + }, { title: "Go to parent session", value: "session.parent", diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/subagent-footer.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/subagent-footer.tsx index 3b7274a01ca4..4cd472e2e7a7 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/subagent-footer.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/subagent-footer.tsx @@ -128,11 +128,11 @@ export function SubagentFooter() { setHover("child")} onMouseOut={() => setHover(null)} - onMouseUp={() => command.trigger("session.child.first")} + onMouseUp={() => command.trigger("session.child.first.subagent")} backgroundColor={hover() === "child" ? theme.backgroundElement : theme.backgroundPanel} > - Child {keybind.print("session_child_first")} + Child {keybind.print("session_child_first_subagent")} diff --git a/packages/opencode/src/config/keybinds.ts b/packages/opencode/src/config/keybinds.ts index a84fc0b37d58..128a92e67e7f 100644 --- a/packages/opencode/src/config/keybinds.ts +++ b/packages/opencode/src/config/keybinds.ts @@ -104,6 +104,7 @@ const KeybindsSchema = Schema.Struct({ history_previous: keybind("up", "Previous history item"), history_next: keybind("down", "Next history item"), session_child_first: keybind("down", "Go to first child session"), + session_child_first_subagent: keybind("down", "Go to first child session (from subagent)"), session_child_cycle: keybind("right", "Go to next child session"), session_child_cycle_reverse: keybind("left", "Go to previous child session"), session_parent: keybind("up", "Go to parent session"),