Skip to content

Commit c65a7d5

Browse files
committed
feat(tui): integrate composer picker
1 parent fa73546 commit c65a7d5

3 files changed

Lines changed: 57 additions & 41 deletions

File tree

‎packages/cli/AGENTS.md‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
```bash
1010
# From packages/cli: local V2 TUI
11-
termctrl start opencode-v2-dev --host opentui --cols 112 --rows 34 -- bun dev --standalone
11+
termctrl start opencode-v2-dev --host opentui --cols 112 --rows 34 -- bun dev
1212

1313
# Released legacy TUI behavior reference
1414
termctrl start opencode-legacy --host opentui --cols 112 --rows 34 -- bunx opencode-ai@latest
@@ -23,12 +23,12 @@ termctrl save opencode-legacy --format png --out /tmp/opencode/legacy.png
2323
## Interactive debugging
2424

2525
- This package is the V2 CLI adapter. Run its `dev` script when testing the TUI; do not use the repository-root `bun dev`, which launches the legacy `packages/opencode` CLI.
26-
- Run commands from `packages/cli`. Use `bun dev --standalone` for most debugging so the TUI starts with a private V2 server instead of depending on the background service.
26+
- Run commands from `packages/cli`. Use `bun dev` for most debugging so the TUI starts with a private V2 server.
2727
- Use `termctrl` for interactive checks instead of starting the TUI as a blocking foreground process. It provides a real PTY, handles OpenTUI's host handshake, and can save reviewable screenshots.
2828
- Use a dedicated session name and do not reuse or kill an unrelated session.
2929

3030
```bash
31-
termctrl start opencode-v2-dev --host opentui --cols 112 --rows 34 -- bun dev --standalone
31+
termctrl start opencode-v2-dev --host opentui --cols 112 --rows 34 -- bun dev
3232
termctrl wait opencode-v2-dev "Ask anything" --timeout 20000
3333
termctrl show opencode-v2-dev
3434
```
@@ -56,7 +56,7 @@ termctrl show opencode-v2-dev
5656
```
5757

5858
- Source changes may require restarting the process. Use `termctrl restart opencode-v2-dev` rather than assuming the running TUI reloaded the change.
59-
- To exercise background-service behavior, omit `--standalone`. Service lifecycle commands are available through `bun dev service start`, `bun dev service status`, and `bun dev service stop`.
59+
- To exercise background-service behavior, use `bun dev service start`, `bun dev service status`, and `bun dev service stop`.
6060
- Always clean up the Terminal Control session when the check is complete:
6161

6262
```bash
@@ -85,7 +85,7 @@ bun dev api <operationId> --param key=value
8585

8686
```bash
8787
termctrl start opencode-v2-debug --host opentui --cols 112 --rows 34 -- \
88-
bun run --inspect=ws://localhost:6499/ src/index.ts --standalone
88+
bun run --inspect=ws://localhost:6499/ src/index.ts
8989
```
9090

9191
- Use `--inspect-wait` or `--inspect-brk` when execution must pause until the debugger attaches.

‎packages/tui/src/config/keybind.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export const Definitions = {
9999
session_toggle_timestamps: keybind("none", "Toggle message timestamps"),
100100
session_toggle_generic_tool_output: keybind("none", "Toggle generic tool output"),
101101
session_queued_prompts: keybind("<leader>q", "Manage queued prompts"),
102-
session_child_first: keybind("<leader>down", "Go to first child session"),
102+
session_child_first: keybind("<leader>down", "Toggle subagent picker"),
103103
session_child_cycle: keybind("right", "Go to next child session"),
104104
session_child_cycle_reverse: keybind("left", "Go to previous child session"),
105105
session_parent: keybind("up", "Go to parent session"),

‎packages/tui/src/routes/session/index.tsx‎

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
import path from "node:path"
1717
import { mkdir, writeFile } from "node:fs/promises"
1818
import { useRoute, useRouteData } from "../../context/route"
19+
import { createStore } from "solid-js/store"
1920
import { useProject } from "../../context/project"
2021
import { useData } from "../../context/data"
2122
import { SplitBorder } from "../../ui/border"
@@ -46,7 +47,7 @@ import { DialogSessionRename } from "../../component/dialog-session-rename"
4647
import { TodoItem } from "../../component/todo-item"
4748
import { DialogMessage } from "./dialog-message"
4849
import { Sidebar } from "./sidebar"
49-
import { SubagentFooter } from "./subagent-footer.tsx"
50+
import { Composer } from "./composer"
5051
import { filetype } from "../../util/filetype"
5152
import parsers from "../../parsers-config"
5253
import { errorMessage } from "../../util/error"
@@ -177,7 +178,10 @@ export function Session() {
177178
if (session()?.parentID) return []
178179
return data.session.question.list(route.sessionID) ?? []
179180
})
180-
const visible = createMemo(() => !session()?.parentID && permissions().length === 0 && questions().length === 0)
181+
const [composer, setComposer] = createStore({
182+
open: false,
183+
tab: undefined as string | undefined,
184+
})
181185
const disabled = createMemo(() => permissions().length > 0 || questions().length > 0)
182186

183187
const pending = createMemo(() => {
@@ -764,11 +768,14 @@ export function Session() {
764768
run: () => unavailable("Backgrounding subagents"),
765769
},
766770
{
767-
title: "Go to child session",
771+
title: "Toggle subagent picker",
768772
value: "session.child.first",
769773
category: "Session",
770-
hidden: true,
771-
run: () => unavailable("Child session discovery"),
774+
run: () => {
775+
if (composer.open || session()?.parentID) setComposer("open", false)
776+
else setComposer("open", true)
777+
dialog.clear()
778+
},
772779
},
773780
{
774781
title: "Go to parent session",
@@ -836,6 +843,7 @@ export function Session() {
836843

837844
// snap to bottom when session changes
838845
createEffect(on(() => route.sessionID, toBottom))
846+
createEffect(on(() => route.sessionID, () => setComposer("open", false)))
839847

840848
return (
841849
<LocationProvider location={location()}>
@@ -898,37 +906,45 @@ export function Session() {
898906
</Show>
899907
</scrollbox>
900908
<box flexShrink={0}>
901-
<Show when={permissions().length > 0}>
902-
<PermissionPrompt request={permissions()[0]} directory={session()?.location.directory} />
903-
</Show>
904-
<Show when={permissions().length === 0 && questions().length > 0}>
905-
<QuestionPrompt request={questions()[0]} directory={session()?.location.directory} />
906-
</Show>
907-
<Show when={session()?.parentID}>
908-
<SubagentFooter />
909-
</Show>
910-
<Show when={visible()}>
911-
<pluginRuntime.Slot
912-
name="session_prompt"
913-
mode="replace"
914-
session_id={route.sessionID}
915-
visible={visible()}
916-
disabled={disabled()}
917-
on_submit={toBottom}
918-
ref={bind}
919-
>
920-
<Prompt
921-
visible={visible()}
909+
<Composer
910+
sessionID={route.sessionID}
911+
open={composer.open || !!session()?.parentID}
912+
defaultTab={composer.tab ?? (session()?.parentID ? "subagents" : undefined)}
913+
onClose={() => setComposer("open", false)}
914+
/>
915+
<Switch>
916+
<Match when={composer.open || !!session()?.parentID}>
917+
{null}
918+
</Match>
919+
<Match when={permissions().length > 0}>
920+
<PermissionPrompt request={permissions()[0]} directory={session()?.location.directory} />
921+
</Match>
922+
<Match when={questions().length > 0}>
923+
<QuestionPrompt request={questions()[0]} directory={session()?.location.directory} />
924+
</Match>
925+
<Match when={!disabled()}>
926+
<pluginRuntime.Slot
927+
name="session_prompt"
928+
mode="replace"
929+
session_id={route.sessionID}
930+
visible={true}
931+
disabled={false}
932+
on_submit={toBottom}
922933
ref={bind}
923-
disabled={disabled()}
924-
onSubmit={() => {
925-
toBottom()
926-
}}
927-
sessionID={route.sessionID}
928-
right={<pluginRuntime.Slot name="session_prompt_right" session_id={route.sessionID} />}
929-
/>
930-
</pluginRuntime.Slot>
931-
</Show>
934+
>
935+
<Prompt
936+
visible={true}
937+
ref={bind}
938+
disabled={false}
939+
onSubmit={() => {
940+
toBottom()
941+
}}
942+
sessionID={route.sessionID}
943+
right={<pluginRuntime.Slot name="session_prompt_right" session_id={route.sessionID} />}
944+
/>
945+
</pluginRuntime.Slot>
946+
</Match>
947+
</Switch>
932948
</box>
933949
</Show>
934950
<Toast />

0 commit comments

Comments
 (0)