From c5d2e0ddf18b9c789896150e1f6d1f3962858109 Mon Sep 17 00:00:00 2001 From: Xubin Ren <52506698+Re-bin@users.noreply.github.com> Date: Mon, 17 Aug 2026 00:25:21 +0800 Subject: [PATCH] fix(tui): restore composer focus after runtime controls --- tui/src/app.test.ts | 7 +++++++ tui/src/app.ts | 5 ++++- tui/src/runtime-controls.ts | 6 +++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/tui/src/app.test.ts b/tui/src/app.test.ts index 871fb6366..c32739269 100644 --- a/tui/src/app.test.ts +++ b/tui/src/app.test.ts @@ -650,6 +650,7 @@ describe("NanobotTui layout", () => { visible: boolean menuRoot: { getChildren(): unknown[] } } + composer: TextareaRenderable titleText: TextRenderable status: TextRenderable meta: TextRenderable @@ -680,8 +681,11 @@ describe("NanobotTui layout", () => { expect(modelRows.every((row) => !row.selectable)).toBe(true) const fast = modelRows.find((row) => row.plainText.includes("fast")) if (!fast) throw new Error("fast model row was not rendered") + ui.composer.blur() + expect(ui.composer.focused).toBe(false) await setup.mockMouse.click(fast.x + 2, fast.y) await waitUntil(() => sent.includes("/model fast")) + expect(ui.composer.focused).toBe(true) await setup.mockMouse.click( ui.runtimeControls.accessText.x + 2, @@ -692,7 +696,10 @@ describe("NanobotTui layout", () => { const accessRows = ui.runtimeControls.menuRoot.getChildren() as TextRenderable[] const full = accessRows.find((row) => row.plainText.includes("Full access")) if (!full) throw new Error("full access row was not rendered") + ui.composer.blur() + expect(ui.composer.focused).toBe(false) await setup.mockMouse.click(full.x + 2, full.y) + expect(ui.composer.focused).toBe(true) expect(scopes).toEqual([{ project_path: "/tmp/nanobot-workspace", diff --git a/tui/src/app.ts b/tui/src/app.ts index a6e6b5b45..5a99a8532 100644 --- a/tui/src/app.ts +++ b/tui/src/app.ts @@ -531,7 +531,10 @@ export class NanobotTui { } }, onStatus: (message) => { this.status.content = message }, - onVisibilityChange: () => this.updateMeta(), + onVisibilityChange: (visible) => { + this.updateMeta() + if (!visible) this.composer.focus() + }, }, ) this.title.add(this.titleText) diff --git a/tui/src/runtime-controls.ts b/tui/src/runtime-controls.ts index 71886a3e4..4ef54e037 100644 --- a/tui/src/runtime-controls.ts +++ b/tui/src/runtime-controls.ts @@ -33,7 +33,7 @@ interface RuntimeControlsOptions { onModel: (name: string) => void onAccess: (scope: WorkspaceScopePayload) => void onStatus: (message: string) => void - onVisibilityChange: () => void + onVisibilityChange: (visible: boolean) => void } /** Model and workspace policy controls expose one small, mouse-optional interface. */ @@ -139,7 +139,7 @@ export class RuntimeControls { this.kind = null this.menu.hide() this.renderColors() - this.options.onVisibilityChange() + this.options.onVisibilityChange(false) } setTheme(theme: RuntimeControlsTheme): void { @@ -261,7 +261,7 @@ export class RuntimeControls { private opened(): void { this.renderColors() - this.options.onVisibilityChange() + this.options.onVisibilityChange(true) } private apply(choice: Choice): void {