From b38f4d79f7af120e5b1392a6d972f44c688287bb Mon Sep 17 00:00:00 2001 From: James Long Date: Thu, 21 May 2026 12:28:07 -0400 Subject: [PATCH] fix(tui): restore diff viewer route and workspace (#28676) --- packages/opencode/src/cli/cmd/tui/app.tsx | 4 +++- .../feature-plugins/system/diff-viewer.tsx | 19 +++++++++++++++---- .../opencode/src/cli/cmd/tui/plugin/api.tsx | 7 +++++++ .../src/cli/cmd/tui/plugin/runtime.ts | 1 + packages/opencode/test/fixture/tui-plugin.ts | 3 +++ packages/plugin/src/tui.ts | 2 +- 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index e326a39b59..ae387c7ab8 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -25,7 +25,7 @@ import { DialogProvider, useDialog } from "@tui/ui/dialog" import { DialogProvider as DialogProviderList } from "@tui/component/dialog-provider" import { ErrorComponent } from "@tui/component/error-component" import { PluginRouteMissing } from "@tui/component/plugin-route-missing" -import { ProjectProvider } from "@tui/context/project" +import { ProjectProvider, useProject } from "@tui/context/project" import { EditorContextProvider } from "@tui/context/editor" import { useEvent } from "@tui/context/event" import { SDKProvider, useSDK } from "@tui/context/sdk" @@ -279,6 +279,7 @@ function App(props: { onSnapshot?: () => Promise }) { const themeState = useTheme() const { theme, mode, setMode, locked, lock, unlock } = themeState const sync = useSync() + const project = useProject() const exit = useExit() const promptRef = usePromptRef() const routes: RouteMap = new Map() @@ -304,6 +305,7 @@ function App(props: { onSnapshot?: () => Promise }) { toast, renderer, attention, + project, }) const [ready, setReady] = createSignal(false) TuiPluginRuntime.init({ diff --git a/packages/opencode/src/cli/cmd/tui/feature-plugins/system/diff-viewer.tsx b/packages/opencode/src/cli/cmd/tui/feature-plugins/system/diff-viewer.tsx index b6dd872cee..6755a65e31 100644 --- a/packages/opencode/src/cli/cmd/tui/feature-plugins/system/diff-viewer.tsx +++ b/packages/opencode/src/cli/cmd/tui/feature-plugins/system/diff-viewer.tsx @@ -1,5 +1,5 @@ /** @jsxImportSource @opentui/solid */ -import type { TuiPlugin, TuiPluginApi } from "@opencode-ai/plugin/tui" +import type { TuiPlugin, TuiPluginApi, TuiRouteCurrent } from "@opencode-ai/plugin/tui" import type { SnapshotFileDiff, VcsFileDiff } from "@opencode-ai/sdk/v2" import type { BoxRenderable, ScrollBoxRenderable } from "@opentui/core" import { LANGUAGE_EXTENSIONS } from "@/lsp/language" @@ -61,7 +61,7 @@ function DiffViewer(props: { api: TuiPluginApi }) { const theme = () => props.api.theme.current const params = () => ("params" in props.api.route.current ? props.api.route.current.params : undefined) as - | { mode?: DiffMode; sessionID?: string; messageID?: string } + | { mode?: DiffMode; sessionID?: string; messageID?: string; returnTo?: TuiRouteCurrent } | undefined const mode = () => params()?.mode ?? "git" const diffInput = createMemo(() => ({ @@ -80,7 +80,10 @@ function DiffViewer(props: { api: TuiPluginApi }) { return normalizeDiffs(result.data ?? []) } - const result = await props.api.client.vcs.diff({ mode: "git" }, { throwOnError: true }) + const result = await props.api.client.vcs.diff( + { mode: "git", workspace: props.api.workspace.current() }, + { throwOnError: true }, + ) return normalizeDiffs(result.data ?? []) }) const files = createMemo(() => diff() ?? []) @@ -250,7 +253,8 @@ function DiffViewer(props: { api: TuiPluginApi }) { title: "Close diff viewer", category: "VCS", run() { - props.api.route.navigate("home") + const target = params()?.returnTo ?? ({ name: "home" } satisfies TuiRouteCurrent) + props.api.route.navigate(target.name, "params" in target ? target.params : undefined) }, }, { @@ -446,6 +450,7 @@ function DiffViewer(props: { api: TuiPluginApi }) { mode: option.value, sessionID: params()?.sessionID, messageID: params()?.messageID, + returnTo: params()?.returnTo, }) }, }))} @@ -672,6 +677,12 @@ const tui: TuiPlugin = async (api) => { api.route.navigate(ROUTE, { mode: "git", sessionID: "params" in api.route.current ? api.route.current.params?.sessionID : undefined, + returnTo: { + name: api.route.current.name, + ...("params" in api.route.current && api.route.current.params + ? { params: api.route.current.params } + : {}), + }, }) api.ui.dialog.clear() }, diff --git a/packages/opencode/src/cli/cmd/tui/plugin/api.tsx b/packages/opencode/src/cli/cmd/tui/plugin/api.tsx index a704286835..1f9058c0f4 100644 --- a/packages/opencode/src/cli/cmd/tui/plugin/api.tsx +++ b/packages/opencode/src/cli/cmd/tui/plugin/api.tsx @@ -8,6 +8,7 @@ import { Dialog as DialogUI, type useDialog } from "@tui/ui/dialog" import type { TuiConfig } from "@/cli/cmd/tui/config/tui" import type { useOpencodeKeymap } from "../keymap" import type { useKV } from "../context/kv" +import type { useProject } from "../context/project" import { DialogAlert } from "../ui/dialog-alert" import { DialogConfirm } from "../ui/dialog-confirm" import { DialogPrompt } from "../ui/dialog-prompt" @@ -41,6 +42,7 @@ type Input = { toast: ReturnType renderer: TuiPluginApi["renderer"] attention: TuiPluginApi["attention"] + project: ReturnType } function routeRegister(routes: RouteMap, list: TuiRouteDefinition[], bump: () => void) { @@ -227,6 +229,11 @@ export function createTuiApi(input: Input): TuiPluginApi { return Keymap.getOpencodeModeStack(input.keymap).push(mode) }, }, + workspace: { + current() { + return input.project.workspace.current() + }, + }, route: { register(list) { return routeRegister(input.routes, list, input.bump) diff --git a/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts b/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts index 515e617563..3e37f5d686 100644 --- a/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts +++ b/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts @@ -628,6 +628,7 @@ function pluginApi(runtime: RuntimeState, plugin: PluginEntry, scope: PluginScop keys: api.keys, keymap, mode: createScopedMode(api.mode, scope), + workspace: api.workspace, route, ui: api.ui, tuiConfig: api.tuiConfig, diff --git a/packages/opencode/test/fixture/tui-plugin.ts b/packages/opencode/test/fixture/tui-plugin.ts index ef18801571..f1fef7cc88 100644 --- a/packages/opencode/test/fixture/tui-plugin.ts +++ b/packages/opencode/test/fixture/tui-plugin.ts @@ -242,6 +242,9 @@ export function createTuiPluginApi(opts: Opts = {}): HostPluginApi { current: () => "base", push: () => () => {}, }, + workspace: { + current: () => undefined, + }, route: { register: () => { if (count) count.route_add += 1 diff --git a/packages/plugin/src/tui.ts b/packages/plugin/src/tui.ts index d78c607f80..496a47b67c 100644 --- a/packages/plugin/src/tui.ts +++ b/packages/plugin/src/tui.ts @@ -579,7 +579,6 @@ export type TuiPluginInstallResult = export type TuiWorkspace = { current: () => string | undefined - set: (workspaceID?: string) => void } export type TuiPluginApi = { @@ -595,6 +594,7 @@ export type TuiPluginApi = { keys: TuiKeys keymap: TuiKeymap mode: TuiModeApi + workspace: TuiWorkspace route: { register: (routes: TuiRouteDefinition[]) => () => void navigate: (name: string, params?: Record) => void