use tokio::sync::{mpsc, oneshot};
pub struct AskRequest {
pub prompt: String,
pub placeholder: Option<String>,
pub secret: bool,
pub options: Vec<String>,
pub reply: oneshot::Sender<AskAnswer>,
}
#[derive(Clone, Debug, Default)]
pub struct AskAnswer {
pub answer: String,
pub cancelled: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum UiCommand {
ShowHelp,
ShowTools,
ManageMcp { arg: Option<String> },
ShowCwd,
SetStatusLayout { arg: Option<String> },
ClearTranscript,
RunShell { command: String },
Quit,
OpenModelOverlay { arg: Option<String> },
OpenEffortOverlay { arg: Option<String> },
SetAgentStatus { status: String },
SetExtensionStatus { ext: String, status: String },
SetExtensionActive {
capability_id: String,
name: String,
activate: bool,
},
}
pub struct UiRequest {
pub command: UiCommand,
pub reply: Option<oneshot::Sender<Vec<String>>>,
}
impl UiRequest {
pub fn fire(command: UiCommand) -> Self {
Self {
command,
reply: None,
}
}
}
pub trait HostUi: Send + Sync {
fn send(&self, command: UiCommand);
fn request(&self, command: UiCommand) -> oneshot::Receiver<Vec<String>>;
}
pub struct TuiHandle {
tx: mpsc::UnboundedSender<UiRequest>,
}
impl TuiHandle {
pub fn new(tx: mpsc::UnboundedSender<UiRequest>) -> Self {
Self { tx }
}
}
impl HostUi for TuiHandle {
fn send(&self, command: UiCommand) {
let _ = self.tx.send(UiRequest::fire(command));
}
fn request(&self, command: UiCommand) -> oneshot::Receiver<Vec<String>> {
let (reply_tx, reply_rx) = oneshot::channel();
let _ = self.tx.send(UiRequest {
command,
reply: Some(reply_tx),
});
reply_rx
}
}