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 {
|
try {
|
||||||
await waitUntil(() => (app as unknown as { ready: boolean }).ready)
|
await waitUntil(() => (app as unknown as { ready: boolean }).ready)
|
||||||
|
app.accept({ event: "goal_status", chat_id: "chat", status: "running" })
|
||||||
await setup.flush()
|
await setup.flush()
|
||||||
await setup.mockMouse.click(
|
await setup.mockMouse.click(
|
||||||
ui.runtimeControls.modelText.x + 2,
|
ui.runtimeControls.modelText.x + 2,
|
||||||
@@ -682,6 +683,8 @@ describe("NanobotTui layout", () => {
|
|||||||
access_mode: "full",
|
access_mode: "full",
|
||||||
restrict_to_workspace: false,
|
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 {
|
} finally {
|
||||||
globalThis.fetch = original
|
globalThis.fetch = original
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -507,7 +507,9 @@ export class NanobotTui {
|
|||||||
modelPreset: this.modelPreset,
|
modelPreset: this.modelPreset,
|
||||||
workspace: options.workspace,
|
workspace: options.workspace,
|
||||||
access: options.access,
|
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(),
|
beforeOpen: () => this.closeTransientMenus(),
|
||||||
refreshScope: () => this.refreshSessionMetadata(this.client.activeChatId),
|
refreshScope: () => this.refreshSessionMetadata(this.client.activeChatId),
|
||||||
onModel: (preset) => this.sendGatewayCommand(`/model ${preset}`, "side_channel", true),
|
onModel: (preset) => this.sendGatewayCommand(`/model ${preset}`, "side_channel", true),
|
||||||
|
|||||||
@@ -49,6 +49,12 @@ export class PickerMenu<T> {
|
|||||||
paddingRight: 1,
|
paddingRight: 1,
|
||||||
backgroundColor: RGBA.defaultBackground(),
|
backgroundColor: RGBA.defaultBackground(),
|
||||||
visible: false,
|
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
|
if (event.button !== 0) return
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
|
this.renderer.clearSelection()
|
||||||
this.selected = index
|
this.selected = index
|
||||||
this.options.onSelect?.(item)
|
this.options.onSelect?.(item)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ interface RuntimeControlsOptions {
|
|||||||
modelPreset: string
|
modelPreset: string
|
||||||
workspace: string
|
workspace: string
|
||||||
access: string
|
access: string
|
||||||
available: () => { ready: boolean; active: boolean }
|
available: () => boolean
|
||||||
beforeOpen: () => void
|
beforeOpen: () => void
|
||||||
refreshScope: () => Promise<void>
|
refreshScope: () => Promise<void>
|
||||||
onModel: (name: string) => void
|
onModel: (name: string) => void
|
||||||
@@ -165,6 +165,7 @@ export class RuntimeControls {
|
|||||||
if (event.button !== 0) return
|
if (event.button !== 0) return
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
|
this.renderer.clearSelection()
|
||||||
open()
|
open()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -199,7 +200,7 @@ export class RuntimeControls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async openModel(): Promise<void> {
|
private async openModel(): Promise<void> {
|
||||||
if (!this.canOpen("Model")) return
|
if (!this.canOpen()) return
|
||||||
try {
|
try {
|
||||||
await this.load(true)
|
await this.load(true)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -222,7 +223,7 @@ export class RuntimeControls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async openAccess(): Promise<void> {
|
private async openAccess(): Promise<void> {
|
||||||
if (!this.canOpen("Access")) return
|
if (!this.canOpen()) return
|
||||||
try {
|
try {
|
||||||
await Promise.all([this.load(true), this.options.refreshScope()])
|
await Promise.all([this.load(true), this.options.refreshScope()])
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -250,12 +251,9 @@ export class RuntimeControls {
|
|||||||
this.opened()
|
this.opened()
|
||||||
}
|
}
|
||||||
|
|
||||||
private canOpen(label: string): boolean {
|
private canOpen(): boolean {
|
||||||
const state = this.options.available()
|
if (this.options.available()) return true
|
||||||
if (state.ready && !state.active) return true
|
this.options.onStatus("Preparing chat…")
|
||||||
this.options.onStatus(state.active
|
|
||||||
? `${label} can be changed between turns`
|
|
||||||
: "Preparing chat…")
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user