mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-09-01 00:31:51 +03:00
fix(tui): keep runtime controls interactive
This commit is contained in:
@@ -653,6 +653,7 @@ describe("NanobotTui layout", () => {
|
||||
|
||||
try {
|
||||
await waitUntil(() => (app as unknown as { ready: boolean }).ready)
|
||||
app.accept({ event: "goal_status", chat_id: "chat", status: "running" })
|
||||
await setup.flush()
|
||||
await setup.mockMouse.click(
|
||||
ui.runtimeControls.modelText.x + 2,
|
||||
@@ -682,6 +683,8 @@ describe("NanobotTui layout", () => {
|
||||
access_mode: "full",
|
||||
restrict_to_workspace: false,
|
||||
}])
|
||||
expect((app as unknown as { activeTurn: boolean }).activeTurn).toBe(true)
|
||||
app.accept({ event: "goal_status", chat_id: "chat", status: "idle" })
|
||||
} finally {
|
||||
globalThis.fetch = original
|
||||
}
|
||||
|
||||
+3
-1
@@ -507,7 +507,9 @@ export class NanobotTui {
|
||||
modelPreset: this.modelPreset,
|
||||
workspace: options.workspace,
|
||||
access: options.access,
|
||||
available: () => ({ ready: this.ready, active: this.activeTurn }),
|
||||
// Runtime settings are session state. Changing them during a turn is
|
||||
// safe and takes effect when the next provider call starts.
|
||||
available: () => this.ready,
|
||||
beforeOpen: () => this.closeTransientMenus(),
|
||||
refreshScope: () => this.refreshSessionMetadata(this.client.activeChatId),
|
||||
onModel: (preset) => this.sendGatewayCommand(`/model ${preset}`, "side_channel", true),
|
||||
|
||||
@@ -49,6 +49,12 @@ export class PickerMenu<T> {
|
||||
paddingRight: 1,
|
||||
backgroundColor: RGBA.defaultBackground(),
|
||||
visible: false,
|
||||
onMouseDown: (event) => {
|
||||
if (event.button !== 0) return
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
this.renderer.clearSelection()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -138,6 +144,7 @@ export class PickerMenu<T> {
|
||||
if (event.button !== 0) return
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
this.renderer.clearSelection()
|
||||
this.selected = index
|
||||
this.options.onSelect?.(item)
|
||||
},
|
||||
|
||||
@@ -27,7 +27,7 @@ interface RuntimeControlsOptions {
|
||||
modelPreset: string
|
||||
workspace: string
|
||||
access: string
|
||||
available: () => { ready: boolean; active: boolean }
|
||||
available: () => boolean
|
||||
beforeOpen: () => void
|
||||
refreshScope: () => Promise<void>
|
||||
onModel: (name: string) => void
|
||||
@@ -165,6 +165,7 @@ export class RuntimeControls {
|
||||
if (event.button !== 0) return
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
this.renderer.clearSelection()
|
||||
open()
|
||||
},
|
||||
})
|
||||
@@ -199,7 +200,7 @@ export class RuntimeControls {
|
||||
}
|
||||
|
||||
private async openModel(): Promise<void> {
|
||||
if (!this.canOpen("Model")) return
|
||||
if (!this.canOpen()) return
|
||||
try {
|
||||
await this.load(true)
|
||||
} catch (error) {
|
||||
@@ -222,7 +223,7 @@ export class RuntimeControls {
|
||||
}
|
||||
|
||||
private async openAccess(): Promise<void> {
|
||||
if (!this.canOpen("Access")) return
|
||||
if (!this.canOpen()) return
|
||||
try {
|
||||
await Promise.all([this.load(true), this.options.refreshScope()])
|
||||
} catch (error) {
|
||||
@@ -250,12 +251,9 @@ export class RuntimeControls {
|
||||
this.opened()
|
||||
}
|
||||
|
||||
private canOpen(label: string): boolean {
|
||||
const state = this.options.available()
|
||||
if (state.ready && !state.active) return true
|
||||
this.options.onStatus(state.active
|
||||
? `${label} can be changed between turns`
|
||||
: "Preparing chat…")
|
||||
private canOpen(): boolean {
|
||||
if (this.options.available()) return true
|
||||
this.options.onStatus("Preparing chat…")
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user