mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-24 13:23:37 +00:00
feat(tui): integrate composer picker
This commit is contained in:
parent
fa73546a86
commit
c65a7d50c1
3 changed files with 57 additions and 41 deletions
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
```bash
|
||||
# From packages/cli: local V2 TUI
|
||||
termctrl start opencode-v2-dev --host opentui --cols 112 --rows 34 -- bun dev --standalone
|
||||
termctrl start opencode-v2-dev --host opentui --cols 112 --rows 34 -- bun dev
|
||||
|
||||
# Released legacy TUI behavior reference
|
||||
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
|
|||
## Interactive debugging
|
||||
|
||||
- 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.
|
||||
- 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.
|
||||
- Run commands from `packages/cli`. Use `bun dev` for most debugging so the TUI starts with a private V2 server.
|
||||
- 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.
|
||||
- Use a dedicated session name and do not reuse or kill an unrelated session.
|
||||
|
||||
```bash
|
||||
termctrl start opencode-v2-dev --host opentui --cols 112 --rows 34 -- bun dev --standalone
|
||||
termctrl start opencode-v2-dev --host opentui --cols 112 --rows 34 -- bun dev
|
||||
termctrl wait opencode-v2-dev "Ask anything" --timeout 20000
|
||||
termctrl show opencode-v2-dev
|
||||
```
|
||||
|
|
@ -56,7 +56,7 @@ termctrl show opencode-v2-dev
|
|||
```
|
||||
|
||||
- Source changes may require restarting the process. Use `termctrl restart opencode-v2-dev` rather than assuming the running TUI reloaded the change.
|
||||
- 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`.
|
||||
- To exercise background-service behavior, use `bun dev service start`, `bun dev service status`, and `bun dev service stop`.
|
||||
- Always clean up the Terminal Control session when the check is complete:
|
||||
|
||||
```bash
|
||||
|
|
@ -85,7 +85,7 @@ bun dev api <operationId> --param key=value
|
|||
|
||||
```bash
|
||||
termctrl start opencode-v2-debug --host opentui --cols 112 --rows 34 -- \
|
||||
bun run --inspect=ws://localhost:6499/ src/index.ts --standalone
|
||||
bun run --inspect=ws://localhost:6499/ src/index.ts
|
||||
```
|
||||
|
||||
- Use `--inspect-wait` or `--inspect-brk` when execution must pause until the debugger attaches.
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ export const Definitions = {
|
|||
session_toggle_timestamps: keybind("none", "Toggle message timestamps"),
|
||||
session_toggle_generic_tool_output: keybind("none", "Toggle generic tool output"),
|
||||
session_queued_prompts: keybind("<leader>q", "Manage queued prompts"),
|
||||
session_child_first: keybind("<leader>down", "Go to first child session"),
|
||||
session_child_first: keybind("<leader>down", "Toggle subagent picker"),
|
||||
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"),
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {
|
|||
import path from "node:path"
|
||||
import { mkdir, writeFile } from "node:fs/promises"
|
||||
import { useRoute, useRouteData } from "../../context/route"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { useProject } from "../../context/project"
|
||||
import { useData } from "../../context/data"
|
||||
import { SplitBorder } from "../../ui/border"
|
||||
|
|
@ -46,7 +47,7 @@ import { DialogSessionRename } from "../../component/dialog-session-rename"
|
|||
import { TodoItem } from "../../component/todo-item"
|
||||
import { DialogMessage } from "./dialog-message"
|
||||
import { Sidebar } from "./sidebar"
|
||||
import { SubagentFooter } from "./subagent-footer.tsx"
|
||||
import { Composer } from "./composer"
|
||||
import { filetype } from "../../util/filetype"
|
||||
import parsers from "../../parsers-config"
|
||||
import { errorMessage } from "../../util/error"
|
||||
|
|
@ -177,7 +178,10 @@ export function Session() {
|
|||
if (session()?.parentID) return []
|
||||
return data.session.question.list(route.sessionID) ?? []
|
||||
})
|
||||
const visible = createMemo(() => !session()?.parentID && permissions().length === 0 && questions().length === 0)
|
||||
const [composer, setComposer] = createStore({
|
||||
open: false,
|
||||
tab: undefined as string | undefined,
|
||||
})
|
||||
const disabled = createMemo(() => permissions().length > 0 || questions().length > 0)
|
||||
|
||||
const pending = createMemo(() => {
|
||||
|
|
@ -764,11 +768,14 @@ export function Session() {
|
|||
run: () => unavailable("Backgrounding subagents"),
|
||||
},
|
||||
{
|
||||
title: "Go to child session",
|
||||
title: "Toggle subagent picker",
|
||||
value: "session.child.first",
|
||||
category: "Session",
|
||||
hidden: true,
|
||||
run: () => unavailable("Child session discovery"),
|
||||
run: () => {
|
||||
if (composer.open || session()?.parentID) setComposer("open", false)
|
||||
else setComposer("open", true)
|
||||
dialog.clear()
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Go to parent session",
|
||||
|
|
@ -836,6 +843,7 @@ export function Session() {
|
|||
|
||||
// snap to bottom when session changes
|
||||
createEffect(on(() => route.sessionID, toBottom))
|
||||
createEffect(on(() => route.sessionID, () => setComposer("open", false)))
|
||||
|
||||
return (
|
||||
<LocationProvider location={location()}>
|
||||
|
|
@ -898,37 +906,45 @@ export function Session() {
|
|||
</Show>
|
||||
</scrollbox>
|
||||
<box flexShrink={0}>
|
||||
<Show when={permissions().length > 0}>
|
||||
<PermissionPrompt request={permissions()[0]} directory={session()?.location.directory} />
|
||||
</Show>
|
||||
<Show when={permissions().length === 0 && questions().length > 0}>
|
||||
<QuestionPrompt request={questions()[0]} directory={session()?.location.directory} />
|
||||
</Show>
|
||||
<Show when={session()?.parentID}>
|
||||
<SubagentFooter />
|
||||
</Show>
|
||||
<Show when={visible()}>
|
||||
<pluginRuntime.Slot
|
||||
name="session_prompt"
|
||||
mode="replace"
|
||||
session_id={route.sessionID}
|
||||
visible={visible()}
|
||||
disabled={disabled()}
|
||||
on_submit={toBottom}
|
||||
ref={bind}
|
||||
>
|
||||
<Prompt
|
||||
visible={visible()}
|
||||
<Composer
|
||||
sessionID={route.sessionID}
|
||||
open={composer.open || !!session()?.parentID}
|
||||
defaultTab={composer.tab ?? (session()?.parentID ? "subagents" : undefined)}
|
||||
onClose={() => setComposer("open", false)}
|
||||
/>
|
||||
<Switch>
|
||||
<Match when={composer.open || !!session()?.parentID}>
|
||||
{null}
|
||||
</Match>
|
||||
<Match when={permissions().length > 0}>
|
||||
<PermissionPrompt request={permissions()[0]} directory={session()?.location.directory} />
|
||||
</Match>
|
||||
<Match when={questions().length > 0}>
|
||||
<QuestionPrompt request={questions()[0]} directory={session()?.location.directory} />
|
||||
</Match>
|
||||
<Match when={!disabled()}>
|
||||
<pluginRuntime.Slot
|
||||
name="session_prompt"
|
||||
mode="replace"
|
||||
session_id={route.sessionID}
|
||||
visible={true}
|
||||
disabled={false}
|
||||
on_submit={toBottom}
|
||||
ref={bind}
|
||||
disabled={disabled()}
|
||||
onSubmit={() => {
|
||||
toBottom()
|
||||
}}
|
||||
sessionID={route.sessionID}
|
||||
right={<pluginRuntime.Slot name="session_prompt_right" session_id={route.sessionID} />}
|
||||
/>
|
||||
</pluginRuntime.Slot>
|
||||
</Show>
|
||||
>
|
||||
<Prompt
|
||||
visible={true}
|
||||
ref={bind}
|
||||
disabled={false}
|
||||
onSubmit={() => {
|
||||
toBottom()
|
||||
}}
|
||||
sessionID={route.sessionID}
|
||||
right={<pluginRuntime.Slot name="session_prompt_right" session_id={route.sessionID} />}
|
||||
/>
|
||||
</pluginRuntime.Slot>
|
||||
</Match>
|
||||
</Switch>
|
||||
</box>
|
||||
</Show>
|
||||
<Toast />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue