Skip to main content
Glama
by 8b-is
dashboard_egui.rs42.9 kB
// egui Dashboard - "Immediate mode, immediate collaboration!" 🎨 // Real-time dashboard for Rust Shell using egui // "Every frame is a fresh start!" - Hue use anyhow::Result; use chrono::{DateTime, Utc}; use egui::{CentralPanel, Context, SidePanel, TopBottomPanel}; use egui::{Color32, Pos2, Stroke, Vec2}; use reqwest::Client; use serde::{Deserialize, Serialize}; use std::cmp::Reverse; use std::collections::VecDeque; use std::sync::{Arc, RwLock}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use tokio::time::sleep; const DEFAULT_STATUS_FEED_URL: &str = "http://127.0.0.1:8430/status/feed"; const STATUS_FEED_POLL_INTERVAL: Duration = Duration::from_secs(5); const STATUS_FEED_ENV_KEYS: [&str; 2] = ["SMART_TREE_G8T_STATUS_FEED", "G8T_STATUS_FEED_URL"]; pub fn default_status_feed_url() -> String { for key in STATUS_FEED_ENV_KEYS { if let Ok(value) = std::env::var(key) { if !value.is_empty() { return value; } } } DEFAULT_STATUS_FEED_URL.to_string() } // ============================================================================ // MCP Integration Types - "The AI can see everything now!" 🔮 // ============================================================================ /// Real-time MCP activity tracking #[derive(Clone, Debug)] pub struct McpActivity { /// Current operation being performed pub current_operation: String, /// Files touched during current operation pub files_touched: Vec<String>, /// Status of the operation pub status: ActivityStatus, /// Progress percentage (0.0 to 1.0) pub progress: f32, /// Operation start time pub started_at: DateTime<Utc>, } #[derive(Clone, Debug, PartialEq)] pub enum ActivityStatus { Idle, Thinking, // AI is processing Reading, // Reading files Writing, // Writing files Searching, // Searching codebase Analyzing, // Deep analysis Waiting, // Waiting for user hint } /// File access event for Wave Compass visualization #[derive(Clone, Debug)] pub struct FileAccessEvent { /// File path that was accessed pub path: String, /// Type of access pub access_type: FileAccessType, /// When it happened pub timestamp: DateTime<Utc>, /// Which MCP tool accessed it pub tool_name: String, /// Duration spent on this file (in ms) pub duration_ms: u64, } #[derive(Clone, Debug, PartialEq)] pub enum FileAccessType { Read, Write, Search, Analyze, } /// Currently executing MCP tool #[derive(Clone, Debug)] pub struct ToolExecution { /// Name of the tool (e.g., "search", "edit", "analyze") pub tool_name: String, /// When it started pub started_at: DateTime<Utc>, /// Tool parameters (JSON-encoded) pub parameters: String, /// Current progress description pub progress: String, } /// User hint for mid-task guidance - "Nudge me gently!" 💫 #[derive(Clone, Debug)] pub struct UserHint { /// Type of hint pub hint_type: HintType, /// When the hint was given pub timestamp: DateTime<Utc>, /// Whether the AI has acknowledged this hint pub acknowledged: bool, } #[derive(Clone, Debug)] pub enum HintType { /// User clicked on a signature/file in Wave Compass Click { path: String, signature: u64 }, /// User typed a text hint TextInput { text: String }, /// User sent voice command Voice { transcript: String, confidence: f32 }, /// User adjusted a parameter slider ParameterAdjust { param_name: String, value: f32 }, } // ============================================================================ // G8T Status Feed Types - "Real-time repo radar" 📡 // ============================================================================ #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct RepoBranchKey { pub owner: String, pub repo: String, pub branch: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct RepoStatusSnapshot { pub push_count: u64, pub pull_count: u64, pub last_commit: Option<String>, pub last_activity: Option<RepoActivityKind>, pub updated_at: u64, } impl Default for RepoStatusSnapshot { fn default() -> Self { Self { push_count: 0, pull_count: 0, last_commit: None, last_activity: None, updated_at: 0, } } } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct RepoStatusUpdate { pub repo: RepoBranchKey, pub totals: RepoStatusSnapshot, pub event: RepoStatusEvent, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub enum RepoStatusEvent { Activity { kind: RepoActivityKind, commit: Option<String>, }, Snapshot, } #[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)] pub enum RepoActivityKind { Push, Pull, } impl Default for McpActivity { fn default() -> Self { Self { current_operation: "Idle".to_string(), files_touched: Vec::new(), status: ActivityStatus::Idle, progress: 0.0, started_at: Utc::now(), } } } /// Dashboard state shared between rust shell and GUI pub struct DashboardState { /// Command history pub command_history: Arc<RwLock<VecDeque<CommandEntry>>>, /// Active displays pub active_displays: Arc<RwLock<Vec<DisplayInfo>>>, /// Voice activity state pub voice_active: Arc<RwLock<bool>>, pub voice_salience: Arc<RwLock<f64>>, /// Memory usage from .m8 files pub memory_usage: Arc<RwLock<MemoryStats>>, /// Chat scanner results pub found_chats: Arc<RwLock<Vec<ChatSource>>>, /// Current casting status pub cast_status: Arc<RwLock<CastStatus>>, /// Ideas buffer - where we both add ideas! pub ideas_buffer: Arc<RwLock<Vec<IdeaEntry>>>, // ======================================================================== // MCP Integration - "Telepathic pair programming!" 🧠↔️🤖 // ======================================================================== /// Real-time MCP activity - what AI is doing RIGHT NOW pub mcp_activity: Arc<RwLock<McpActivity>>, /// File access log for Wave Compass lighting up pub file_access_log: Arc<RwLock<Vec<FileAccessEvent>>>, /// Currently executing MCP tool (None if idle) pub active_tool: Arc<RwLock<Option<ToolExecution>>>, /// User hints queue - nudges for the AI during execution pub user_hints: Arc<RwLock<VecDeque<UserHint>>>, /// WebSocket connection count (for status display) pub ws_connections: Arc<RwLock<usize>>, /// Cached g8t repo status feed snapshot pub repo_status_feed: Arc<RwLock<Vec<RepoStatusUpdate>>>, /// Currently configured status feed endpoint pub status_feed_endpoint: Arc<RwLock<String>>, } impl DashboardState { pub fn status_feed_endpoint(&self) -> String { self.status_feed_endpoint .read() .map(|value| value.clone()) .unwrap_or_else(|_| DEFAULT_STATUS_FEED_URL.to_string()) } pub fn set_status_feed_endpoint(&self, endpoint: String) { if let Ok(mut current) = self.status_feed_endpoint.write() { *current = endpoint; } } pub fn update_status_feed(&self, updates: Vec<RepoStatusUpdate>) { if let Ok(mut feed) = self.repo_status_feed.write() { *feed = updates; } } } #[derive(Clone)] pub struct CommandEntry { pub timestamp: String, pub command: String, pub output: String, pub success: bool, } #[derive(Clone)] pub struct DisplayInfo { pub name: String, pub display_type: String, pub resolution: (u16, u16), pub active: bool, } #[derive(Clone)] pub struct MemoryStats { pub total_memories: usize, pub token_efficiency: f32, pub backwards_position: usize, pub importance_scores: Vec<(String, f32)>, } #[derive(Clone)] pub struct ChatSource { pub platform: String, pub count: usize, pub last_updated: String, } #[derive(Clone)] pub struct CastStatus { pub casting_to: Option<String>, pub content_type: String, pub latency_ms: f32, } #[derive(Clone)] pub struct IdeaEntry { pub author: String, // "Hue" or "Aye" pub idea: String, pub timestamp: String, pub implemented: bool, pub priority: Priority, } #[derive(Clone, PartialEq)] pub enum Priority { Now, // Do it immediately! Soon, // Next few commits Later, // When we get time Maybe, // Cool but not essential } /// The egui dashboard app pub struct Dashboard { state: Arc<DashboardState>, // UI state selected_tab: Tab, command_input: String, idea_input: String, show_raw_memory: bool, voice_graph: VecDeque<f32>, // Wave compass for drift visualization (Omni's contribution!) wave_compass: crate::wave_compass::WaveCompass, // Theme dark_mode: bool, // G8T feed configuration g8t_endpoint_input: String, } #[derive(PartialEq)] enum Tab { Overview, Displays, Memory, Voice, Ideas, WaveCompass, // Omni's wave drift visualizer! McpActivity, // Real-time AI collaboration! 🤖💫 G8tStatus, // g8t repo activity feed 📡 Debug, } impl Dashboard { pub fn new(state: Arc<DashboardState>) -> Self { let g8t_endpoint_input = state.status_feed_endpoint(); Self { state, selected_tab: Tab::Overview, command_input: String::new(), idea_input: String::new(), show_raw_memory: false, voice_graph: VecDeque::with_capacity(100), wave_compass: crate::wave_compass::WaveCompass::new(), dark_mode: true, g8t_endpoint_input, } } /// Main update function called each frame pub fn update_ui(&mut self, ctx: &Context, _frame: &mut eframe::Frame) { // Top panel with tabs TopBottomPanel::top("top_panel").show(ctx, |ui| { ui.horizontal(|ui| { ui.heading("🚀 Rust Shell Dashboard"); ui.separator(); ui.selectable_value(&mut self.selected_tab, Tab::Overview, "Overview"); ui.selectable_value(&mut self.selected_tab, Tab::Displays, "Displays"); ui.selectable_value(&mut self.selected_tab, Tab::Memory, "Memory"); ui.selectable_value(&mut self.selected_tab, Tab::Voice, "Voice"); ui.selectable_value(&mut self.selected_tab, Tab::Ideas, "💡 Ideas"); ui.selectable_value(&mut self.selected_tab, Tab::WaveCompass, "🧭 Waves"); ui.selectable_value(&mut self.selected_tab, Tab::McpActivity, "🤖 MCP Live"); ui.selectable_value(&mut self.selected_tab, Tab::G8tStatus, "g8t Fleet"); ui.selectable_value(&mut self.selected_tab, Tab::Debug, "Debug"); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { if ui.button(if self.dark_mode { "☀" } else { "🌙" }).clicked() { self.dark_mode = !self.dark_mode; } }); }); }); // Side panel for quick status SidePanel::left("side_panel").show(ctx, |ui| { ui.heading("Status"); ui.separator(); // Voice indicator let voice_active = self.state.voice_active.read().unwrap(); if *voice_active { ui.colored_label(Color32::GREEN, "🎤 Voice Active"); } else { ui.label("🔇 Voice Inactive"); } // Cast status let cast_status = self.state.cast_status.read().unwrap(); if let Some(target) = &cast_status.casting_to { ui.colored_label(Color32::BLUE, format!("📺 Casting to {}", target)); ui.label(format!("Latency: {:.1}ms", cast_status.latency_ms)); } // Memory stats let mem_stats = self.state.memory_usage.read().unwrap(); ui.separator(); ui.label(format!("Memories: {}", mem_stats.total_memories)); ui.label(format!( "Token Eff: {:.1}%", mem_stats.token_efficiency * 100.0 )); // Chat sources ui.separator(); ui.label("Chat Sources:"); let chats = self.state.found_chats.read().unwrap(); for chat in chats.iter() { ui.label(format!(" {} ({})", chat.platform, chat.count)); } }); // Central panel with selected tab content CentralPanel::default().show(ctx, |ui| match self.selected_tab { Tab::Overview => self.show_overview(ui), Tab::Displays => self.show_displays(ui), Tab::Memory => self.show_memory(ui), Tab::Voice => self.show_voice(ui), Tab::Ideas => self.show_ideas(ui), Tab::WaveCompass => self.show_wave_compass(ui), Tab::McpActivity => self.show_mcp_activity(ui), Tab::G8tStatus => self.show_g8t_status(ui), Tab::Debug => self.show_debug(ui), }); // Bottom panel for command input TopBottomPanel::bottom("bottom_panel").show(ctx, |ui| { ui.horizontal(|ui| { ui.label("Command:"); let response = ui.text_edit_singleline(&mut self.command_input); if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) { self.execute_command(); } if ui.button("Execute").clicked() { self.execute_command(); } }); }); } fn show_overview(&mut self, ui: &mut egui::Ui) { ui.heading("System Overview"); ui.separator(); // Command history ui.collapsing("Recent Commands", |ui| { let history = self.state.command_history.read().unwrap(); for entry in history.iter().rev().take(10) { let color = if entry.success { Color32::GREEN } else { Color32::RED }; ui.horizontal(|ui| { ui.colored_label(color, &entry.timestamp); ui.monospace(&entry.command); }); if !entry.output.is_empty() { ui.add_space(10.0); ui.monospace(&entry.output); } } }); // Active displays grid ui.separator(); ui.heading("Active Displays"); let displays = self.state.active_displays.read().unwrap(); ui.horizontal_wrapped(|ui| { for display in displays.iter() { ui.group(|ui| { ui.vertical(|ui| { ui.label(&display.name); ui.label(&display.display_type); ui.label(format!("{}x{}", display.resolution.0, display.resolution.1)); if display.active { ui.colored_label(Color32::GREEN, "Active"); } }); }); } }); } fn show_displays(&mut self, ui: &mut egui::Ui) { ui.heading("Display Management"); ui.separator(); if ui.button("🔍 Discover Displays").clicked() { // Trigger display discovery // This would call rust_shell.discover_displays() } ui.separator(); // Display list with cast controls let displays = self.state.active_displays.read().unwrap(); for display in displays.iter() { ui.group(|ui| { ui.horizontal(|ui| { ui.label(&display.name); ui.label(format!("({})", display.display_type)); if ui.button("Cast").clicked() { // Trigger cast to this display } if display.active { if ui.button("Stop").clicked() { // Stop casting } } }); }); } } fn show_memory(&mut self, ui: &mut egui::Ui) { ui.heading("Memory System (.m8)"); ui.separator(); let mem_stats = self.state.memory_usage.read().unwrap(); // Memory visualization ui.label(format!("Total Memories: {}", mem_stats.total_memories)); ui.label(format!( "Backwards Position: {}", mem_stats.backwards_position )); ui.separator(); ui.checkbox(&mut self.show_raw_memory, "Show Raw Memory"); if self.show_raw_memory { ui.group(|ui| { ui.monospace("0x80: node_modules"); ui.monospace("0x81: .git"); ui.monospace("0x82: target"); ui.monospace("0x91: .rs"); ui.monospace("0xFFFE: Claude"); }); } // Importance scores ui.separator(); ui.heading("Important Memories"); for (memory, score) in mem_stats.importance_scores.iter().take(5) { ui.horizontal(|ui| { // Draw importance bar let bar_width = 200.0 * score; let (rect, _response) = ui.allocate_exact_size(Vec2::new(bar_width, 20.0), egui::Sense::hover()); ui.painter() .rect_filled(rect, 0.0, Color32::from_rgb(100, 200, 100)); ui.label(memory); ui.label(format!("{:.1}%", score * 100.0)); }); } } fn show_voice(&mut self, ui: &mut egui::Ui) { ui.heading("Voice Activity (Marine Algorithm)"); ui.separator(); let voice_active = *self.state.voice_active.read().unwrap(); let salience = *self.state.voice_salience.read().unwrap(); // Voice status ui.horizontal(|ui| { if voice_active { ui.colored_label(Color32::GREEN, "🎤 VOICE DETECTED"); } else { ui.label("🔇 No Voice"); } ui.label(format!("Salience: {:.2}", salience)); }); // Voice activity graph ui.separator(); ui.label("Voice Activity Graph:"); // Update graph data self.voice_graph.push_back(salience as f32); if self.voice_graph.len() > 100 { self.voice_graph.pop_front(); } // Draw graph let graph_size = Vec2::new(400.0, 100.0); let (rect, _response) = ui.allocate_exact_size(graph_size, egui::Sense::hover()); let painter = ui.painter(); painter.rect_filled(rect, 0.0, Color32::from_gray(20)); if self.voice_graph.len() > 1 { let points: Vec<Pos2> = self .voice_graph .iter() .enumerate() .map(|(i, &val)| { let x = rect.left() + (i as f32 / 100.0) * rect.width(); let y = rect.bottom() - val * rect.height(); Pos2::new(x, y) }) .collect(); for window in points.windows(2) { painter.line_segment([window[0], window[1]], Stroke::new(2.0, Color32::GREEN)); } } // Threshold line let threshold_y = rect.bottom() - 0.5 * rect.height(); painter.line_segment( [ Pos2::new(rect.left(), threshold_y), Pos2::new(rect.right(), threshold_y), ], Stroke::new(1.0, Color32::RED), ); } fn show_ideas(&mut self, ui: &mut egui::Ui) { ui.heading("💡 Ideas Buffer - Collaborative Brainstorming!"); ui.separator(); // Input for new ideas ui.horizontal(|ui| { ui.label("New Idea:"); ui.text_edit_singleline(&mut self.idea_input); if ui.button("Add Hue's Idea").clicked() { self.add_idea("Hue", Priority::Soon); } if ui.button("Add Aye's Idea").clicked() { self.add_idea("Aye", Priority::Soon); } }); ui.separator(); // Ideas list let mut ideas = self.state.ideas_buffer.write().unwrap(); // Group by priority for priority in [ Priority::Now, Priority::Soon, Priority::Later, Priority::Maybe, ] { let priority_ideas: Vec<_> = ideas .iter() .filter(|i| i.priority == priority) .cloned() .collect(); if !priority_ideas.is_empty() { let header = match priority { Priority::Now => "🔥 DO IT NOW!", Priority::Soon => "⚡ Soon", Priority::Later => "📅 Later", Priority::Maybe => "🤔 Maybe", }; ui.collapsing(header, |ui| { for idea in priority_ideas { ui.group(|ui| { ui.horizontal(|ui| { let author_color = if idea.author == "Hue" { Color32::from_rgb(100, 150, 255) } else { Color32::from_rgb(255, 150, 100) }; ui.colored_label(author_color, &idea.author); ui.label(&idea.timestamp); if idea.implemented { ui.colored_label(Color32::GREEN, "✓"); } }); ui.label(&idea.idea); ui.horizontal(|ui| { if !idea.implemented { if ui.button("Mark Done").clicked() { // Find and mark as implemented if let Some(idx) = ideas.iter().position(|i| i.idea == idea.idea) { ideas[idx].implemented = true; } } } }); }); } }); } } } fn show_wave_compass(&mut self, ui: &mut egui::Ui) { // Gather current directory signatures (would come from actual scanning) // For now, let's create some example signatures from our semantic categories let signatures = vec![ crate::wave_compass::WaveSig::from_quantum( "src".into(), &crate::quantum_wave_signature::QuantumWaveSignature::from_raw(0x73A9E2F5), ), crate::wave_compass::WaveSig::from_quantum( "tests".into(), &crate::quantum_wave_signature::QuantumWaveSignature::from_raw(0x9F2E6B31), ), crate::wave_compass::WaveSig::from_quantum( "mcp".into(), &crate::quantum_wave_signature::QuantumWaveSignature::from_raw(0x2C7DB5A3), ), crate::wave_compass::WaveSig::from_quantum( "mem8".into(), &crate::quantum_wave_signature::QuantumWaveSignature::from_raw(0xE4739AC2), ), crate::wave_compass::WaveSig::from_quantum( "formatters".into(), &crate::quantum_wave_signature::QuantumWaveSignature::from_raw(0xA7E2C94D), ), crate::wave_compass::WaveSig::from_quantum( "generated".into(), &crate::quantum_wave_signature::QuantumWaveSignature::from_raw(0xD2B847A6), ), ]; // Update compass with current signatures self.wave_compass.update(signatures); // Show the compass self.wave_compass.show(ui); // Additional controls ui.separator(); ui.horizontal(|ui| { if ui.button("🔄 Refresh").clicked() { // Would trigger a rescan of directories } ui.label("Resonance Threshold:"); let mut threshold = 0.5_f32; ui.add(egui::Slider::new(&mut threshold, 0.0..=1.0)); if ui.button("📸 Snapshot").clicked() { // Would save current state to .m8 file } }); // Show detected resonances ui.separator(); ui.heading("Detected Resonances"); let resonances = crate::wave_compass::find_resonances(&self.wave_compass.signatures, 0.5); for res in resonances { let sig1 = &self.wave_compass.signatures[res.sig1_idx]; let sig2 = &self.wave_compass.signatures[res.sig2_idx]; let emoji = if res.is_harmonic { "🎵" } else { "🌊" }; ui.label(format!( "{} {} ↔ {} ({:.0}% resonance)", emoji, sig1.name, sig2.name, res.strength * 100.0 )); } } fn show_mcp_activity(&mut self, ui: &mut egui::Ui) { ui.heading("🤖 Real-Time AI Collaboration - \"I can see you seeing me!\""); ui.separator(); let mcp_activity = self.state.mcp_activity.read().unwrap(); let active_tool = self.state.active_tool.read().unwrap(); let file_log = self.state.file_access_log.read().unwrap(); let hints = self.state.user_hints.read().unwrap(); // Current AI Status - Big and Bold! ui.group(|ui| { ui.vertical(|ui| { // Status with color coding let (status_text, status_color) = match mcp_activity.status { ActivityStatus::Idle => ("💤 Idle", Color32::GRAY), ActivityStatus::Thinking => { ("🧠 Thinking...", Color32::from_rgb(150, 100, 255)) } ActivityStatus::Reading => { ("📖 Reading Files", Color32::from_rgb(100, 200, 255)) } ActivityStatus::Writing => { ("✍️ Writing Code", Color32::from_rgb(255, 200, 100)) } ActivityStatus::Searching => { ("🔍 Searching...", Color32::from_rgb(100, 255, 200)) } ActivityStatus::Analyzing => { ("🔬 Deep Analysis", Color32::from_rgb(255, 100, 200)) } ActivityStatus::Waiting => { ("⏸️ Waiting for You", Color32::from_rgb(255, 255, 100)) } }; ui.horizontal(|ui| { ui.heading(status_text); ui.colored_label(status_color, "●"); // Status indicator }); // Current operation if !mcp_activity.current_operation.is_empty() { ui.label(format!("Operation: {}", mcp_activity.current_operation)); } // Progress bar if mcp_activity.progress > 0.0 { let progress_bar = egui::ProgressBar::new(mcp_activity.progress) .text(format!("{:.0}%", mcp_activity.progress * 100.0)); ui.add(progress_bar); } // Duration let duration = Utc::now().signed_duration_since(mcp_activity.started_at); ui.label(format!( "Duration: {}.{}s", duration.num_seconds(), duration.num_milliseconds() % 1000 )); // Files touched in current operation if !mcp_activity.files_touched.is_empty() { ui.label(format!( "Files touched: {}", mcp_activity.files_touched.len() )); ui.collapsing("Show files", |ui| { for file in &mcp_activity.files_touched { ui.monospace(file); } }); } }); }); ui.separator(); // Active Tool Details if let Some(tool) = active_tool.as_ref() { ui.heading("🔧 Active Tool"); ui.group(|ui| { ui.label(format!("Tool: {}", tool.tool_name)); ui.label(format!("Progress: {}", tool.progress)); ui.collapsing("Parameters", |ui| { ui.monospace(&tool.parameters); }); let tool_duration = Utc::now().signed_duration_since(tool.started_at); ui.label(format!( "Running for: {}.{}s", tool_duration.num_seconds(), tool_duration.num_milliseconds() % 1000 )); }); ui.separator(); } // File Access Log with Timeline ui.heading("📂 File Access Timeline"); ui.label(format!("Total accesses: {}", file_log.len())); egui::ScrollArea::vertical() .max_height(200.0) .show(ui, |ui| { // Show recent file accesses (last 50) for event in file_log.iter().rev().take(50) { let (icon, color) = match event.access_type { FileAccessType::Read => ("📖", Color32::from_rgb(100, 200, 255)), FileAccessType::Write => ("✍️", Color32::from_rgb(255, 200, 100)), FileAccessType::Search => ("🔍", Color32::from_rgb(100, 255, 200)), FileAccessType::Analyze => ("🔬", Color32::from_rgb(255, 100, 200)), }; ui.horizontal(|ui| { ui.colored_label(color, icon); ui.label(event.timestamp.format("%H:%M:%S%.3f").to_string()); ui.monospace(&event.path); ui.label(format!("{}ms", event.duration_ms)); ui.label(format!("({})", event.tool_name)); }); } }); ui.separator(); // User Hints Section - "Nudge the AI!" ui.heading("💫 Your Hints to AI"); ui.label("Send nudges without stopping the AI!"); ui.horizontal(|ui| { if ui.button("👆 Click Hint").clicked() { // This would be triggered when user clicks on Wave Compass ui.label("Click on Wave Compass signatures to send location hints!"); } if ui.button("💬 Text Hint").clicked() { // Open text input dialog } if ui.button("🎤 Voice Hint").clicked() { // Trigger voice input } }); // Show pending hints if !hints.is_empty() { ui.separator(); ui.label(format!("Pending hints: {}", hints.len())); for hint in hints.iter().rev().take(5) { let (icon, hint_text) = match &hint.hint_type { HintType::Click { path, signature } => { ("👆", format!("Clicked: {} (sig: {:X})", path, signature)) } HintType::TextInput { text } => ("💬", format!("Text: {}", text)), HintType::Voice { transcript, confidence, } => ( "🎤", format!("Voice: {} ({:.0}%)", transcript, confidence * 100.0), ), HintType::ParameterAdjust { param_name, value } => { ("🎚️", format!("Adjust {}: {:.2}", param_name, value)) } }; ui.horizontal(|ui| { ui.label(icon); ui.label(hint.timestamp.format("%H:%M:%S").to_string()); ui.label(hint_text); if hint.acknowledged { ui.colored_label(Color32::GREEN, "✓ Seen"); } else { ui.colored_label(Color32::YELLOW, "⏳ Pending"); } }); } } ui.separator(); // WebSocket Connection Status let ws_count = *self.state.ws_connections.read().unwrap(); ui.horizontal(|ui| { ui.label("WebSocket connections:"); if ws_count > 0 { ui.colored_label(Color32::GREEN, format!("{} active", ws_count)); } else { ui.colored_label(Color32::RED, "Disconnected"); } }); } fn show_g8t_status(&mut self, ui: &mut egui::Ui) { ui.heading("g8t Fleet Activity"); ui.separator(); let mut apply_endpoint = false; ui.horizontal(|ui| { ui.label("Feed URL:"); if self.g8t_endpoint_input.is_empty() { self.g8t_endpoint_input = self.state.status_feed_endpoint(); } let response = ui.text_edit_singleline(&mut self.g8t_endpoint_input); if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) { apply_endpoint = true; } if ui.button("Apply").clicked() { apply_endpoint = true; } if ui.button("Refresh").clicked() { let state_clone = self.state.clone(); tokio::spawn(async move { if let Err(err) = fetch_status_feed_once(state_clone).await { eprintln!("⚠️ Failed to refresh g8t status feed: {}", err); } }); } }); if apply_endpoint { let endpoint = self.g8t_endpoint_input.trim().to_string(); if !endpoint.is_empty() { self.state.set_status_feed_endpoint(endpoint.clone()); println!("🌐 Updated g8t status endpoint to {}", endpoint); } } ui.separator(); let mut feed = self .state .repo_status_feed .read() .map(|entries| entries.clone()) .unwrap_or_default(); if feed.is_empty() { ui.label("No g8t activity yet. Push or pull via g8t to light this up!"); return; } feed.sort_by_key(|update| Reverse(update.totals.updated_at)); let total_repos = feed.len(); let total_pushes: u64 = feed.iter().map(|u| u.totals.push_count).sum(); let total_pulls: u64 = feed.iter().map(|u| u.totals.pull_count).sum(); ui.horizontal(|ui| { ui.label(format!("Repos tracked: {}", total_repos)); ui.separator(); ui.label(format!("Pushes: {}", total_pushes)); ui.separator(); ui.label(format!("Pulls: {}", total_pulls)); if let Some(latest) = feed.first() { ui.separator(); ui.label(format!( "Last activity: {}", format_relative_time(latest.totals.updated_at) )); } }); ui.separator(); egui::ScrollArea::vertical() .id_source("g8t_status_scroll") .show(ui, |ui| { egui::Grid::new("g8t_status_grid") .striped(true) .num_columns(6) .show(ui, |ui| { ui.strong("Repository"); ui.strong("Branch"); ui.strong("Push/Pull"); ui.strong("Last Commit"); ui.strong("Activity"); ui.strong("Updated"); ui.end_row(); for update in feed.iter().take(50) { let repo_name = format!("{}/{}", update.repo.owner, update.repo.repo); ui.label(repo_name); ui.label(update.repo.branch.clone()); ui.label(format!( "{}/{}", update.totals.push_count, update.totals.pull_count )); ui.label(short_commit(&update.totals.last_commit)); let (icon, label, color) = summarize_activity(update.totals.last_activity); ui.colored_label(color, format!("{} {}", icon, label)); ui.label(format_relative_time(update.totals.updated_at)); ui.end_row(); } }); }); } fn show_debug(&mut self, ui: &mut egui::Ui) { ui.heading("Debug Information"); ui.separator(); ui.monospace(format!( "FPS: {:.1}", ui.ctx().input(|i| i.stable_dt).recip() )); ui.monospace(format!("Time: {:.3}s", ui.ctx().input(|i| i.time))); ui.separator(); ui.collapsing("Memory Layout", |ui| { ui.monospace("DashboardState size: {} bytes"); ui.monospace("Ideas buffer capacity: {}"); }); } fn execute_command(&mut self) { if self.command_input.is_empty() { return; } // Add to history let mut history = self.state.command_history.write().unwrap(); history.push_back(CommandEntry { timestamp: chrono::Local::now().format("%H:%M:%S").to_string(), command: self.command_input.clone(), output: String::new(), // Would be filled by actual execution success: true, }); // Keep history limited if history.len() > 100 { history.pop_front(); } self.command_input.clear(); } fn add_idea(&mut self, author: &str, priority: Priority) { if self.idea_input.is_empty() { return; } let mut ideas = self.state.ideas_buffer.write().unwrap(); ideas.push(IdeaEntry { author: author.to_string(), idea: self.idea_input.clone(), timestamp: chrono::Local::now().format("%H:%M:%S").to_string(), implemented: false, priority, }); self.idea_input.clear(); } } /// Start the egui dashboard pub async fn start_dashboard(state: Arc<DashboardState>) -> Result<()> { // Start background poller for g8t status feed tokio::spawn(poll_status_feed(state.clone())); let options = eframe::NativeOptions { viewport: egui::ViewportBuilder::default() .with_inner_size([800.0, 600.0]) .with_title("Rust Shell Dashboard"), ..Default::default() }; eframe::run_native( "Rust Shell Dashboard", options, Box::new(|cc| { // Configure fonts and style let mut style = (*cc.egui_ctx.style()).clone(); style.visuals.dark_mode = true; cc.egui_ctx.set_style(style); Ok(Box::new(Dashboard::new(state))) }), ) .map_err(|e| anyhow::anyhow!("Failed to start dashboard: {}", e)) } impl eframe::App for Dashboard { fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { self.update_ui(ctx, frame); // Request repaint for animations ctx.request_repaint(); } } async fn poll_status_feed(state: Arc<DashboardState>) { loop { if let Err(err) = fetch_status_feed_once(state.clone()).await { eprintln!("⚠️ Status feed polling error: {}", err); } sleep(STATUS_FEED_POLL_INTERVAL).await; } } async fn fetch_status_feed_once(state: Arc<DashboardState>) -> Result<()> { let endpoint = state.status_feed_endpoint(); let client = Client::new(); let response = client.get(&endpoint).send().await?; if !response.status().is_success() { anyhow::bail!( "Status feed request failed with {} for {}", response.status(), endpoint ); } let updates: Vec<RepoStatusUpdate> = response.json().await?; state.update_status_feed(updates); Ok(()) } fn summarize_activity(activity: Option<RepoActivityKind>) -> (&'static str, &'static str, Color32) { match activity { Some(RepoActivityKind::Push) => ("⬆", "Push", Color32::from_rgb(120, 220, 120)), Some(RepoActivityKind::Pull) => ("⬇", "Pull", Color32::from_rgb(120, 180, 255)), None => ("…", "Idle", Color32::GRAY), } } fn short_commit(commit: &Option<String>) -> String { commit .as_ref() .map(|hash| { if hash.len() <= 8 { hash.clone() } else { hash.chars().take(8).collect::<String>() } }) .unwrap_or_else(|| "—".to_string()) } fn format_relative_time(timestamp_ms: u64) -> String { if timestamp_ms == 0 { return "never".to_string(); } let now = current_millis(); if timestamp_ms >= now { return "just now".to_string(); } let diff = now - timestamp_ms; let seconds = diff / 1000; if seconds < 5 { return "just now".to_string(); } if seconds < 60 { return format!("{}s ago", seconds); } let minutes = seconds / 60; if minutes < 60 { return format!("{}m ago", minutes); } let hours = minutes / 60; if hours < 24 { return format!("{}h ago", hours); } let days = hours / 24; format!("{}d ago", days) } fn current_millis() -> u64 { SystemTime::now() .duration_since(UNIX_EPOCH) .map(|dur| dur.as_millis() as u64) .unwrap_or_default() }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/8b-is/smart-tree'

If you have feedback or need assistance with the MCP directory API, please join our Discord server