From 8dc9ac07a2dd02cbf374475be97a0d42447c0bac Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Mon, 6 Jul 2026 16:51:23 -0500 Subject: [PATCH] fix(tui): sync v2 terminal title (#35610) --- packages/tui/src/app.tsx | 2 +- packages/tui/test/app-lifecycle.test.tsx | 31 ++++++++++++++++++++---- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/packages/tui/src/app.tsx b/packages/tui/src/app.tsx index 67d16c1c48..fa36ad1839 100644 --- a/packages/tui/src/app.tsx +++ b/packages/tui/src/app.tsx @@ -500,7 +500,7 @@ function App(props: { onSnapshot?: () => Promise; pluginHost: TuiPlugi } if (route.data.type === "session") { - const session = sync.session.get(route.data.sessionID) + const session = data.session.get(route.data.sessionID) if (!session || isDefaultTitle(session.title)) { renderer.setTerminalTitle("OpenCode") return diff --git a/packages/tui/test/app-lifecycle.test.tsx b/packages/tui/test/app-lifecycle.test.tsx index 30601bb528..c4302e2790 100644 --- a/packages/tui/test/app-lifecycle.test.tsx +++ b/packages/tui/test/app-lifecycle.test.tsx @@ -58,10 +58,24 @@ test("SIGHUP clears title and disposes scoped resources once", async () => { } }) -test("app.exit prints the session epilogue after scoped cleanup", async () => { +test("session lifecycle updates the terminal title and prints the epilogue after cleanup", async () => { const setup = await createTestRenderer({ width: 80, height: 24, useThread: false }) const core = await import("@opentui/core") mock.module("@opentui/core", () => ({ ...core, createCliRenderer: async () => setup.renderer })) + let initialTitle!: () => void + const initialTitleSet = new Promise((resolve) => { + initialTitle = resolve + }) + let renamedTitle!: () => void + const renamedTitleSet = new Promise((resolve) => { + renamedTitle = resolve + }) + const setTitle = setup.renderer.setTerminalTitle.bind(setup.renderer) + setup.renderer.setTerminalTitle = (title) => { + if (title === "OC | Demo session") initialTitle() + if (title === "OC | Renamed session") renamedTitle() + setTitle(title) + } const events = createEventStream() const calls = createFetch((url) => { if (url.pathname === "/api/session") @@ -100,7 +114,7 @@ test("app.exit prints the session epilogue after scoped cleanup", async () => { client: createClient(calls.fetch), api: createApi(calls.fetch), config: createTuiResolvedConfig({ plugin_enabled: {} }), - args: { continue: true }, + args: { sessionID: "dummy" }, pluginHost: { async start(input) { api = input.api @@ -112,12 +126,19 @@ test("app.exit prints the session epilogue after scoped cleanup", async () => { ) await ready - await setup.renderOnce() - await setup.renderOnce() + await initialTitleSet + events.emit({ + id: "evt_renamed", + created: 1, + type: "session.renamed", + durable: { aggregateID: "dummy", seq: 1, version: 1 }, + data: { sessionID: "dummy", title: "Renamed session" }, + }) + await renamedTitleSet api?.keymap.dispatchCommand("app.exit") await task - expect(stdout).toContain("Demo session") + expect(stdout).toContain("Renamed session") expect(stdout).toContain("opencode -s dummy") } finally { process.stdout.write = originalWrite