diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index b18030f1..76da0fbf 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -905,6 +905,120 @@ export namespace Server { }), async (c) => c.json(await callTui(c)), ) + .post( + "/tui/open-sessions", + describeRoute({ + description: "Open the session dialog", + operationId: "tui.openSessions", + responses: { + 200: { + description: "Session dialog opened successfully", + content: { + "application/json": { + schema: resolver(z.boolean()), + }, + }, + }, + }, + }), + async (c) => c.json(await callTui(c)), + ) + .post( + "/tui/open-themes", + describeRoute({ + description: "Open the theme dialog", + operationId: "tui.openThemes", + responses: { + 200: { + description: "Theme dialog opened successfully", + content: { + "application/json": { + schema: resolver(z.boolean()), + }, + }, + }, + }, + }), + async (c) => c.json(await callTui(c)), + ) + .post( + "/tui/open-models", + describeRoute({ + description: "Open the model dialog", + operationId: "tui.openModels", + responses: { + 200: { + description: "Model dialog opened successfully", + content: { + "application/json": { + schema: resolver(z.boolean()), + }, + }, + }, + }, + }), + async (c) => c.json(await callTui(c)), + ) + .post( + "/tui/submit-prompt", + describeRoute({ + description: "Submit the prompt", + operationId: "tui.submitPrompt", + responses: { + 200: { + description: "Prompt submitted successfully", + content: { + "application/json": { + schema: resolver(z.boolean()), + }, + }, + }, + }, + }), + async (c) => c.json(await callTui(c)), + ) + .post( + "/tui/clear-prompt", + describeRoute({ + description: "Clear the prompt", + operationId: "tui.clearPrompt", + responses: { + 200: { + description: "Prompt cleared successfully", + content: { + "application/json": { + schema: resolver(z.boolean()), + }, + }, + }, + }, + }), + async (c) => c.json(await callTui(c)), + ) + .post( + "/tui/execute-command", + describeRoute({ + description: "Execute a TUI command (e.g. switch_mode)", + operationId: "tui.executeCommand", + responses: { + 200: { + description: "Command executed successfully", + content: { + "application/json": { + schema: resolver(z.boolean()), + }, + }, + }, + }, + }), + zValidator( + "json", + z.object({ + command: z.string(), + }), + ), + async (c) => c.json(await callTui(c)), + ) .route("/tui/control", TuiRoute) return result diff --git a/packages/sdk/stainless/stainless.yml b/packages/sdk/stainless/stainless.yml index ab40f877..b9d69cde 100644 --- a/packages/sdk/stainless/stainless.yml +++ b/packages/sdk/stainless/stainless.yml @@ -134,7 +134,13 @@ resources: tui: methods: appendPrompt: post /tui/append-prompt + submitPrompt: post /tui/submit-prompt + clearPrompt: post /tui/clear-prompt openHelp: post /tui/open-help + openSessions: post /tui/open-sessions + openThemes: post /tui/open-themes + openModels: post /tui/open-models + executeCommand: post /tui/execute-command settings: disable_mock_tests: true diff --git a/packages/tui/cmd/opencode/main.go b/packages/tui/cmd/opencode/main.go index 66888fe4..5532289f 100644 --- a/packages/tui/cmd/opencode/main.go +++ b/packages/tui/cmd/opencode/main.go @@ -86,7 +86,7 @@ func main() { logger := slog.New(apiHandler) slog.SetDefault(logger) - slog.Debug("TUI launched", "app", appInfoStr, "modes", modesStr) + slog.Debug("TUI launched", "app", appInfoStr, "modes", modesStr, "url", url) go func() { err = clipboard.Init() diff --git a/packages/tui/internal/tui/tui.go b/packages/tui/internal/tui/tui.go index 9b6ec7ea..0b87872f 100644 --- a/packages/tui/internal/tui/tui.go +++ b/packages/tui/internal/tui/tui.go @@ -609,6 +609,15 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case "/tui/open-help": helpDialog := dialog.NewHelpDialog(a.app) a.modal = helpDialog + case "/tui/open-sessions": + sessionDialog := dialog.NewSessionDialog(a.app) + a.modal = sessionDialog + case "/tui/open-themes": + themeDialog := dialog.NewThemeDialog() + a.modal = themeDialog + case "/tui/open-models": + modelDialog := dialog.NewModelDialog(a.app) + a.modal = modelDialog case "/tui/append-prompt": var body struct { Text string `json:"text"` @@ -620,6 +629,34 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { text = " " + text } a.editor.SetValueWithAttachments(existing + text + " ") + case "/tui/submit-prompt": + updated, cmd := a.editor.Submit() + a.editor = updated.(chat.EditorComponent) + cmds = append(cmds, cmd) + case "/tui/clear-prompt": + updated, cmd := a.editor.Clear() + a.editor = updated.(chat.EditorComponent) + cmds = append(cmds, cmd) + case "/tui/execute-command": + var body struct { + Command string `json:"command"` + } + json.Unmarshal((msg.Body), &body) + command := commands.Command{} + for _, cmd := range a.app.Commands { + if string(cmd.Name) == body.Command { + command = cmd + break + } + } + if command.Name == "" { + slog.Error("Invalid command passed to /tui/execute-command", "command", body.Command) + return a, nil + } + updated, cmd := a.executeCommand(commands.Command(command)) + a = updated.(Model) + cmds = append(cmds, cmd) + default: break }