use crate::web_dashboard::state_sync::{AccessType, McpActivityState, UserHint, UserHintsQueue};
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::RwLock;
#[derive(Clone)]
pub struct DashboardBridge {
activity: Arc<RwLock<McpActivityState>>,
hints: Arc<RwLock<UserHintsQueue>>,
}
impl DashboardBridge {
pub fn new(
activity: Arc<RwLock<McpActivityState>>,
hints: Arc<RwLock<UserHintsQueue>>,
) -> Self {
Self { activity, hints }
}
pub async fn tool_started(&self, tool_name: &str, parameters: serde_json::Value) {
let mut activity = self.activity.write().await;
activity.start_tool(tool_name, parameters);
}
pub async fn tool_progress(&self, progress: f32) {
let mut activity = self.activity.write().await;
activity.update_progress(progress);
}
pub async fn file_accessed(&self, path: PathBuf, access_type: AccessType, tool_name: &str) {
let mut activity = self.activity.write().await;
activity.record_file_access(path, access_type, tool_name);
}
pub async fn file_read(&self, path: impl Into<PathBuf>, tool_name: &str) {
self.file_accessed(path.into(), AccessType::Read, tool_name)
.await;
}
pub async fn file_written(&self, path: impl Into<PathBuf>, tool_name: &str) {
self.file_accessed(path.into(), AccessType::Write, tool_name)
.await;
}
pub async fn file_analyzed(&self, path: impl Into<PathBuf>, tool_name: &str) {
self.file_accessed(path.into(), AccessType::Analyze, tool_name)
.await;
}
pub async fn path_searched(&self, path: impl Into<PathBuf>, tool_name: &str) {
self.file_accessed(path.into(), AccessType::Search, tool_name)
.await;
}
pub async fn tool_completed(&self, success: bool, summary: &str) {
let mut activity = self.activity.write().await;
activity.complete_tool(success, summary);
}
pub async fn set_operation(&self, operation: &str) {
let mut activity = self.activity.write().await;
activity.current_operation = operation.to_string();
}
pub async fn consume_hint(&self) -> Option<UserHint> {
let mut hints = self.hints.write().await;
hints.consume_next()
}
pub async fn peek_hints(&self) -> Vec<UserHint> {
let hints = self.hints.read().await;
hints.peek_unconsumed().into_iter().cloned().collect()
}
pub async fn has_pending_hints(&self) -> bool {
let hints = self.hints.read().await;
hints.unconsumed_count() > 0
}
pub async fn get_activity_snapshot(&self) -> McpActivitySnapshot {
let activity = self.activity.read().await;
McpActivitySnapshot {
active_tool: activity.active_tool.as_ref().map(|t| t.name.clone()),
current_operation: activity.current_operation.clone(),
files_touched_count: activity.files_touched.len(),
directories_explored_count: activity.directories_explored.len(),
tools_executed_count: activity.tool_history.len(),
}
}
}
#[derive(Debug, Clone)]
pub struct McpActivitySnapshot {
pub active_tool: Option<String>,
pub current_operation: String,
pub files_touched_count: usize,
pub directories_explored_count: usize,
pub tools_executed_count: usize,
}
#[macro_export]
macro_rules! with_dashboard_bridge {
($ctx:expr, $tool_name:expr, $params:expr, $body:expr) => {{
if let Some(ref bridge) = $ctx.dashboard_bridge {
bridge.tool_started($tool_name, $params.clone()).await;
}
let result = $body;
if let Some(ref bridge) = $ctx.dashboard_bridge {
match &result {
Ok(val) => {
let summary = match val.as_str() {
Some(s) if s.len() > 100 => format!("{}...", &s[..100]),
Some(s) => s.to_string(),
None => "OK".to_string(),
};
bridge.tool_completed(true, &summary).await;
}
Err(e) => {
bridge.tool_completed(false, &e.to_string()).await;
}
}
}
result
}};
}