fix(tui): use SDK publish for acks and per-instance state tracking

- TUI ack events now use sdk.client.tui.publish() instead of Bus.publish()
  to properly route events from TUI process to backend worker thread
- TerminalControl now uses Instance.state() for per-instance subscription
  tracking instead of global boolean, fixing multi-instance scenarios
- Remove unused Bus import from app.tsx
pull/14293/head
rentiansheng 2026-02-20 08:48:49 +08:00
parent 44d0519137
commit 31e7749b43
No known key found for this signature in database
GPG Key ID: 9C3653AF125722B0
2 changed files with 32 additions and 14 deletions

View File

@ -49,7 +49,6 @@ import { ToastProvider, useToast } from "./ui/toast"
import { ExitProvider, useExit } from "./context/exit"
import { Session as SessionApi } from "@/session"
import { TuiEvent } from "./event"
import { Bus } from "@/bus"
import { KVProvider, useKV } from "./context/kv"
import { Provider } from "@/provider/provider"
import { ArgsProvider, useArgs, type Args } from "./context/args"
@ -881,14 +880,24 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
sdk.event.on(TuiEvent.RendererSuspendRequest.type, async (evt) => {
renderer.suspend()
renderer.currentRenderBuffer.clear()
await Bus.publish(TuiEvent.RendererSuspendAck, { token: evt.properties.token })
await sdk.client.tui.publish({
body: {
type: TuiEvent.RendererSuspendAck.type,
properties: { token: evt.properties.token },
},
})
})
sdk.event.on(TuiEvent.RendererResumeRequest.type, async (evt) => {
renderer.currentRenderBuffer.clear()
renderer.resume()
renderer.requestRender()
await Bus.publish(TuiEvent.RendererResumeAck, { token: evt.properties.token })
await sdk.client.tui.publish({
body: {
type: TuiEvent.RendererResumeAck.type,
properties: { token: evt.properties.token },
},
})
})
return (

View File

@ -1,6 +1,7 @@
import { Bus } from "@/bus"
import { TuiEvent } from "@/cli/cmd/tui/event"
import { Identifier } from "@/id/id"
import { Instance } from "@/project/instance"
import { Log } from "@/util/log"
const log = Log.create({ service: "terminal-control" })
@ -23,7 +24,15 @@ export namespace TerminalControl {
readonly timer: NodeJS.Timeout
}
const pendingAcks = new Map<string, AckHandler>()
interface State {
pendingAcks: Map<string, AckHandler>
subscriptionsInitialized: boolean
}
const state = Instance.state(() => ({
pendingAcks: new Map<string, AckHandler>(),
subscriptionsInitialized: false,
}))
function isTuiMode(): boolean {
return process.env.OPENCODE_TUI === "1"
@ -36,17 +45,17 @@ export namespace TerminalControl {
function waitForAck(token: string, timeoutMs: number, operation: "suspend" | "resume"): Promise<void> {
return new Promise<void>((resolve) => {
const timer = setTimeout(() => {
pendingAcks.delete(token)
state().pendingAcks.delete(token)
log.warn(`${operation}: timeout waiting for TUI ack`, { token, timeoutMs })
resolve()
}, timeoutMs)
pendingAcks.set(token, {
state().pendingAcks.set(token, {
token,
timer,
resolve: () => {
clearTimeout(timer)
pendingAcks.delete(token)
state().pendingAcks.delete(token)
log.debug(`${operation}: received ack`, { token })
resolve()
},
@ -55,10 +64,10 @@ export namespace TerminalControl {
}
function cleanupPendingAck(token: string): void {
const handler = pendingAcks.get(token)
const handler = state().pendingAcks.get(token)
if (handler) {
clearTimeout(handler.timer)
pendingAcks.delete(token)
state().pendingAcks.delete(token)
}
}
@ -101,14 +110,14 @@ export namespace TerminalControl {
}
function handleAck(token: string): void {
const handler = pendingAcks.get(token)
const handler = state().pendingAcks.get(token)
handler?.resolve()
}
let subscriptionsInitialized = false
function ensureSubscriptions(): void {
if (subscriptionsInitialized) return
subscriptionsInitialized = true
const s = state()
if (s.subscriptionsInitialized) return
s.subscriptionsInitialized = true
Bus.subscribe(TuiEvent.RendererSuspendAck, (event) => {
handleAck(event.properties.token)
@ -118,6 +127,6 @@ export namespace TerminalControl {
handleAck(event.properties.token)
})
log.debug("Bus subscriptions initialized")
log.debug("Bus subscriptions initialized", { directory: Instance.directory })
}
}