fix(tui): set mini terminal title on Linux (#38261)
This commit is contained in:
parent
ced3d5e02a
commit
0e08b7330f
5 changed files with 52 additions and 0 deletions
|
|
@ -82,6 +82,7 @@ export type Lifecycle = {
|
|||
footer: FooterApi
|
||||
onResize(fn: () => void): () => void
|
||||
refreshTheme(): void
|
||||
setTitle(title?: string): void
|
||||
resetForReplay(input: { sessionTitle?: string; sessionID?: string; history: RunPrompt[] }): Promise<void>
|
||||
close(input: { showExit: boolean; sessionTitle?: string; sessionID?: string; history?: RunPrompt[] }): Promise<void>
|
||||
}
|
||||
|
|
@ -184,6 +185,12 @@ export async function createRuntimeLifecycle(input: LifecycleInput): Promise<Lif
|
|||
consoleMode: "disabled",
|
||||
clearOnShutdown: false,
|
||||
})
|
||||
const setTitle = (title?: string) => {
|
||||
if (input.host.platform !== "linux") return
|
||||
if (!title || isDefaultTitle(title)) return renderer.setTerminalTitle("OpenCode")
|
||||
renderer.setTerminalTitle(`OC | ${title.length > 40 ? title.slice(0, 37) + "..." : title}`)
|
||||
}
|
||||
setTitle(input.sessionTitle)
|
||||
const theme = await resolveRunTheme(renderer, tuiConfig.theme, mono)
|
||||
renderer.setBackgroundColor(theme.background)
|
||||
const state: SplashState = {
|
||||
|
|
@ -338,6 +345,7 @@ export async function createRuntimeLifecycle(input: LifecycleInput): Promise<Lif
|
|||
footer.close()
|
||||
await footer.idle().catch(() => {})
|
||||
footer.destroy()
|
||||
if (input.host.platform === "linux") renderer.setTerminalTitle("")
|
||||
shutdown(renderer)
|
||||
if (!wroteExit) {
|
||||
input.host.stdout.write("\n")
|
||||
|
|
@ -350,6 +358,7 @@ export async function createRuntimeLifecycle(input: LifecycleInput): Promise<Lif
|
|||
refreshTheme() {
|
||||
footer.refreshTheme()
|
||||
},
|
||||
setTitle,
|
||||
onResize(fn) {
|
||||
let width = renderer.terminalWidth
|
||||
let height = renderer.terminalHeight
|
||||
|
|
|
|||
|
|
@ -443,6 +443,7 @@ async function runInteractiveRuntime(input: RunRuntimeInput, deps: RunRuntimeDep
|
|||
if (footer.isClosed || runtimeController.signal.aborted) return
|
||||
state.sessionID = next.sessionID
|
||||
state.sessionTitle = next.sessionTitle ?? state.sessionTitle
|
||||
shell.setTitle(state.sessionTitle)
|
||||
state.agent = next.agent
|
||||
state.location = next.location
|
||||
state.model = next.model
|
||||
|
|
@ -741,6 +742,10 @@ async function runInteractiveRuntime(input: RunRuntimeInput, deps: RunRuntimeDep
|
|||
replayLimit: input.replayLimit,
|
||||
footer,
|
||||
onCommit: rememberLocal,
|
||||
onSessionTitle: (title) => {
|
||||
state.sessionTitle = title
|
||||
shell.setTitle(title)
|
||||
},
|
||||
trace: log,
|
||||
onCatalogRefresh: requestCatalogRefresh,
|
||||
contextLimit: (model) =>
|
||||
|
|
@ -887,6 +892,7 @@ async function runInteractiveRuntime(input: RunRuntimeInput, deps: RunRuntimeDep
|
|||
state.shown = false
|
||||
state.sessionID = created.sessionID
|
||||
state.sessionTitle = created.sessionTitle
|
||||
shell.setTitle(state.sessionTitle)
|
||||
state.agent = created.agent ?? state.agent
|
||||
state.location = created.location
|
||||
state.model = created.model
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ type StreamInput = {
|
|||
replayLimit?: number
|
||||
footer: FooterApi
|
||||
onCommit?: (commit: StreamCommit) => void
|
||||
onSessionTitle?: (title: string) => void
|
||||
trace?: Trace
|
||||
signal?: AbortSignal
|
||||
onCatalogRefresh?: (signal?: AbortSignal) => unknown | Promise<unknown>
|
||||
|
|
@ -828,6 +829,10 @@ export async function createSessionTransport(input: StreamInput): Promise<Sessio
|
|||
}
|
||||
input.trace?.write("recv.event", event)
|
||||
subagents.main(client, event, attempt.signal)
|
||||
if (event.type === "session.renamed") {
|
||||
input.onSessionTitle?.(event.data.title)
|
||||
return
|
||||
}
|
||||
if (event.type === "session.input.admitted") {
|
||||
if (event.data.input.type !== "user") return
|
||||
mergePending({
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ describe("run interactive runtime", () => {
|
|||
footer: api,
|
||||
onResize: () => () => {},
|
||||
refreshTheme: () => {},
|
||||
setTitle: () => {},
|
||||
resetForReplay: () => Promise.resolve(),
|
||||
close: () => Promise.resolve(),
|
||||
}
|
||||
|
|
@ -180,6 +181,7 @@ describe("run interactive runtime", () => {
|
|||
footer: api,
|
||||
onResize: () => () => {},
|
||||
refreshTheme: () => {},
|
||||
setTitle: () => {},
|
||||
resetForReplay: () => Promise.resolve(),
|
||||
close: () => Promise.resolve(),
|
||||
}
|
||||
|
|
@ -199,6 +201,7 @@ describe("run interactive runtime", () => {
|
|||
const lifecycleStarted = defer<void>()
|
||||
const painted = defer<void>()
|
||||
const events: FooterEvent[] = []
|
||||
const titles: Array<string | undefined> = []
|
||||
const api = footer(events)
|
||||
api.idle = () => painted.promise
|
||||
const event = api.event
|
||||
|
|
@ -265,6 +268,7 @@ describe("run interactive runtime", () => {
|
|||
footer: api,
|
||||
onResize: () => () => {},
|
||||
refreshTheme: () => {},
|
||||
setTitle: (title) => titles.push(title),
|
||||
resetForReplay: () => Promise.resolve(),
|
||||
close: () => Promise.resolve(),
|
||||
}
|
||||
|
|
@ -282,6 +286,7 @@ describe("run interactive runtime", () => {
|
|||
history: [{ text: "previous prompt", parts: [] }],
|
||||
})
|
||||
expect(events).toContainEqual({ type: "agent", agent: "review" })
|
||||
expect(titles).toEqual(["Resume"])
|
||||
expect(events).toContainEqual({
|
||||
type: "model",
|
||||
model: "Little Frank · OpenAI · high",
|
||||
|
|
@ -344,6 +349,7 @@ describe("run interactive runtime", () => {
|
|||
footer: api,
|
||||
onResize: () => () => {},
|
||||
refreshTheme: () => {},
|
||||
setTitle: () => {},
|
||||
resetForReplay: () => Promise.resolve(),
|
||||
close: async (input) => {
|
||||
closedTitle = input.sessionTitle
|
||||
|
|
@ -423,6 +429,7 @@ describe("run interactive runtime", () => {
|
|||
footer: api,
|
||||
onResize: () => () => {},
|
||||
refreshTheme: () => {},
|
||||
setTitle: () => {},
|
||||
resetForReplay: () => Promise.resolve(),
|
||||
close: () => Promise.resolve(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -200,6 +200,31 @@ afterEach(() => {
|
|||
})
|
||||
|
||||
describe("V2 mini transport", () => {
|
||||
test("reports session title changes", async () => {
|
||||
const events = feed()
|
||||
events.push(connected())
|
||||
const titles: string[] = []
|
||||
const transport = await createSessionTransport({
|
||||
sdk: sdk({ streams: [events] }),
|
||||
sessionID: "ses_1",
|
||||
thinking: false,
|
||||
footer: footer().api,
|
||||
onSessionTitle: (title) => titles.push(title),
|
||||
})
|
||||
|
||||
events.push({
|
||||
id: "evt_renamed",
|
||||
created: 1,
|
||||
type: "session.renamed",
|
||||
durable: durable("ses_1", 1),
|
||||
data: { sessionID: "ses_1", title: "Greeting" },
|
||||
})
|
||||
|
||||
while (titles.length === 0) await Bun.sleep(0)
|
||||
expect(titles).toEqual(["Greeting"])
|
||||
await transport.close()
|
||||
})
|
||||
|
||||
test("formats footer usage with compact tokens and context percentage", async () => {
|
||||
const events = feed()
|
||||
events.push(connected())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue