diff --git a/crates/g3-console/src/process/detector.rs b/crates/g3-console/src/process/detector.rs index b2883c8..9b488f7 100644 --- a/crates/g3-console/src/process/detector.rs +++ b/crates/g3-console/src/process/detector.rs @@ -3,7 +3,7 @@ use anyhow::Result; use chrono::{DateTime, Utc}; use std::path::PathBuf; use sysinfo::{System, Pid, Process}; -use tracing::{debug, warn}; +use tracing::{debug, info, warn}; pub struct ProcessDetector { system: System, @@ -17,7 +17,11 @@ impl ProcessDetector { } pub fn detect_instances(&mut self) -> Result> { - self.system.refresh_processes(); + info!("Scanning for g3 processes..."); + // Refresh all processes to ensure we catch newly started ones + // Using refresh_all() instead of just refresh_processes() to ensure + // we get complete information about new processes + self.system.refresh_all(); let mut instances = Vec::new(); // Find all g3 processes @@ -33,7 +37,7 @@ impl ProcessDetector { } } - debug!("Detected {} g3 instances", instances.len()); + info!("Detected {} g3 instances", instances.len()); Ok(instances) } @@ -168,7 +172,7 @@ impl ProcessDetector { } pub fn get_process_status(&mut self, pid: u32) -> Option { - self.system.refresh_processes(); + self.system.refresh_all(); let sysinfo_pid = Pid::from_u32(pid); if self.system.process(sysinfo_pid).is_some() { diff --git a/crates/g3-console/web/index.html b/crates/g3-console/web/index.html index 4f9cfb3..53c9052 100644 --- a/crates/g3-console/web/index.html +++ b/crates/g3-console/web/index.html @@ -15,7 +15,7 @@
-

G3 Console

+

G3 Console ● LIVE

diff --git a/crates/g3-console/web/js/router.js b/crates/g3-console/web/js/router.js index 66a52ff..cfe77a5 100644 --- a/crates/g3-console/web/js/router.js +++ b/crates/g3-console/web/js/router.js @@ -6,6 +6,7 @@ const router = { currentInstanceId: null, initialized: false, renderInProgress: false, + REFRESH_INTERVAL_MS: 3000, // Refresh every 3 seconds for live updates init() { console.log('[Router] init() called'); @@ -84,6 +85,9 @@ const router = { this.renderInProgress = true; try { + // Flash live indicator + this.flashLiveIndicator(); + // Check if we already have a container for instances let instancesList = container.querySelector('.instances-list'); const isInitialLoad = !instancesList; @@ -167,11 +171,11 @@ const router = { // Schedule next refresh only if still on home route if (this.currentRoute === '/' || this.currentRoute === '') { - console.log('[Router] Scheduling auto-refresh in 5 seconds'); + console.log(`[Router] Scheduling auto-refresh in ${this.REFRESH_INTERVAL_MS}ms`); this.refreshTimeout = setTimeout(() => { console.log('[Router] Auto-refresh triggered'); this.renderHome(container); - }, 5000); + }, this.REFRESH_INTERVAL_MS); } } catch (error) { console.error('[Router] Error in renderHome:', error); @@ -187,12 +191,26 @@ const router = { } }, + flashLiveIndicator() { + const indicator = document.getElementById('live-indicator'); + if (indicator) { + indicator.style.animation = 'none'; + // Force reflow + void indicator.offsetWidth; + indicator.style.animation = null; + indicator.style.opacity = '1'; + } + }, + async renderDetail(container, id) { console.log('[Router] renderDetail called for', id); this.currentInstanceId = id; try { + // Flash live indicator + this.flashLiveIndicator(); + // Check if we already have a detail view for this instance let detailView = container.querySelector('.detail-view'); const isInitialLoad = !detailView || detailView.getAttribute('data-instance-id') !== id; diff --git a/crates/g3-console/web/styles/app.css b/crates/g3-console/web/styles/app.css index 1fb8e3a..ee58385 100644 --- a/crates/g3-console/web/styles/app.css +++ b/crates/g3-console/web/styles/app.css @@ -64,6 +64,22 @@ body { color: var(--text-primary); } +.live-indicator { + font-size: 0.625rem; /* 75% of 0.833rem */ + font-weight: 600; + color: var(--success); + margin-left: 0.75rem; + display: inline-flex; + align-items: center; + gap: 0.25rem; + animation: pulse 2s ease-in-out infinite; +} + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.5; } +} + .header-actions { display: flex; gap: 1rem;