summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJames Long <[email protected]>2026-02-27 09:41:23 -0500
committerGitHub <[email protected]>2026-02-27 09:41:23 -0500
commit3dc10a1c165e0a8c567718c33ddd8a62814e0c14 (patch)
tree69ff84d7d0fa6bb991b31706fb1605c2c0f5faa1
parent157920b2fbfbfb46dae8badb8328a53f6904e510 (diff)
downloadopencode-3dc10a1c165e0a8c567718c33ddd8a62814e0c14.tar.gz
opencode-3dc10a1c165e0a8c567718c33ddd8a62814e0c14.zip
Change keybindings to navigate between child sessions (#14814)
-rw-r--r--packages/opencode/src/cli/cmd/tui/routes/session/index.tsx84
-rw-r--r--packages/opencode/src/config/config.ts7
2 files changed, 62 insertions, 29 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 f20267e08..314018367 100644
--- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
+++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
@@ -48,6 +48,7 @@ import type { SkillTool } from "@/tool/skill"
import { useKeyboard, useRenderer, useTerminalDimensions, type JSX } from "@opentui/solid"
import { useSDK } from "@tui/context/sdk"
import { useCommandDialog } from "@tui/component/dialog-command"
+import type { DialogContext } from "@tui/ui/dialog"
import { useKeybind } from "@tui/context/keybind"
import { Header } from "./header"
import { parsePatch } from "diff"
@@ -226,6 +227,8 @@ export function Session() {
let scroll: ScrollBoxRenderable
let prompt: PromptRef
const keybind = useKeybind()
+ const dialog = useDialog()
+ const renderer = useRenderer()
// Allow exit when in child session (prompt is hidden)
const exit = useExit()
@@ -312,19 +315,40 @@ export function Session() {
const local = useLocal()
+ function moveFirstChild() {
+ if (children().length === 1) return
+ const next = children().find((x) => !!x.parentID)
+ if (next) {
+ navigate({
+ type: "session",
+ sessionID: next.id,
+ })
+ }
+ }
+
function moveChild(direction: number) {
if (children().length === 1) return
- let next = children().findIndex((x) => x.id === session()?.id) + direction
- if (next >= children().length) next = 0
- if (next < 0) next = children().length - 1
- if (children()[next]) {
+
+ const sessions = children().filter((x) => !!x.parentID)
+ let next = sessions.findIndex((x) => x.id === session()?.id) + direction
+
+ if (next >= sessions.length) next = 0
+ if (next < 0) next = sessions.length - 1
+ if (sessions[next]) {
navigate({
type: "session",
- sessionID: children()[next].id,
+ sessionID: sessions[next].id,
})
}
}
+ function childSessionHandler(func: (dialog: DialogContext) => void) {
+ return (dialog: DialogContext) => {
+ if (!session()?.parentID || dialog.stack.length > 0) return
+ func(dialog)
+ }
+ }
+
const command = useCommandDialog()
command.register(() => [
{
@@ -884,24 +908,13 @@ export function Session() {
},
},
{
- title: "Next child session",
- value: "session.child.next",
- keybind: "session_child_cycle",
- category: "Session",
- hidden: true,
- onSelect: (dialog) => {
- moveChild(1)
- dialog.clear()
- },
- },
- {
- title: "Previous child session",
- value: "session.child.previous",
- keybind: "session_child_cycle_reverse",
+ title: "Go to child session",
+ value: "session.child.first",
+ keybind: "session_child_first",
category: "Session",
hidden: true,
onSelect: (dialog) => {
- moveChild(-1)
+ moveFirstChild()
dialog.clear()
},
},
@@ -911,7 +924,7 @@ export function Session() {
keybind: "session_parent",
category: "Session",
hidden: true,
- onSelect: (dialog) => {
+ onSelect: childSessionHandler((dialog) => {
const parentID = session()?.parentID
if (parentID) {
navigate({
@@ -920,7 +933,29 @@ export function Session() {
})
}
dialog.clear()
- },
+ }),
+ },
+ {
+ title: "Next child session",
+ value: "session.child.next",
+ keybind: "session_child_cycle",
+ category: "Session",
+ hidden: true,
+ onSelect: childSessionHandler((dialog) => {
+ moveChild(1)
+ dialog.clear()
+ }),
+ },
+ {
+ title: "Previous child session",
+ value: "session.child.previous",
+ keybind: "session_child_cycle_reverse",
+ category: "Session",
+ hidden: true,
+ onSelect: childSessionHandler((dialog) => {
+ moveChild(-1)
+ dialog.clear()
+ }),
},
])
@@ -971,9 +1006,6 @@ export function Session() {
}
})
- const dialog = useDialog()
- const renderer = useRenderer()
-
// snap to bottom when session changes
createEffect(on(() => route.sessionID, toBottom))
@@ -1933,7 +1965,7 @@ function Task(props: ToolProps<typeof TaskTool>) {
</box>
<Show when={props.metadata.sessionId}>
<text fg={theme.text}>
- {keybind.print("session_child_cycle")}
+ {keybind.print("session_child_first")}
<span style={{ fg: theme.textMuted }}> view subagents</span>
</text>
</Show>
diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts
index 28aea4d67..141f61569 100644
--- a/packages/opencode/src/config/config.ts
+++ b/packages/opencode/src/config/config.ts
@@ -896,9 +896,10 @@ export namespace Config {
.describe("Delete word backward in input"),
history_previous: z.string().optional().default("up").describe("Previous history item"),
history_next: z.string().optional().default("down").describe("Next history item"),
- session_child_cycle: z.string().optional().default("<leader>right").describe("Next child session"),
- session_child_cycle_reverse: z.string().optional().default("<leader>left").describe("Previous child session"),
- session_parent: z.string().optional().default("<leader>up").describe("Go to parent session"),
+ session_child_first: z.string().optional().default("<leader>down").describe("Go to first child session"),
+ 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"),
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("<leader>h").describe("Toggle tips on home screen"),