// ========================================== // GLOBAL NOTIFICATION SYSTEM // ========================================== // Floating notification panel accessible from any view /** * Initialize global notification panel */ function initGlobalNotifications() { // Create FAB and panel if not exists if (!document.getElementById('globalNotificationFab')) { const fabHtml = `
🔔 0
🔔 Notifications
No notifications

System events and task updates will appear here

`; const container = document.createElement('div'); container.id = 'globalNotificationContainer'; container.innerHTML = fabHtml; document.body.appendChild(container); } renderGlobalNotifications(); } /** * Toggle notification panel visibility */ function toggleGlobalNotifications() { isNotificationPanelVisible = !isNotificationPanelVisible; const panel = document.getElementById('globalNotificationPanel'); const fab = document.getElementById('globalNotificationFab'); if (panel && fab) { if (isNotificationPanelVisible) { panel.classList.add('show'); fab.classList.add('active'); } else { panel.classList.remove('show'); fab.classList.remove('active'); } } } /** * Add a global notification * @param {string} type - 'info', 'success', 'warning', 'error' * @param {string} message - Main notification message * @param {string} details - Optional details * @param {string} source - Optional source identifier (e.g., 'explorer', 'mcp') */ function addGlobalNotification(type, message, details = null, source = null) { const notification = { id: Date.now(), type, message, details, source, timestamp: new Date().toISOString(), read: false }; globalNotificationQueue.unshift(notification); // Keep only last 100 notifications if (globalNotificationQueue.length > 100) { globalNotificationQueue = globalNotificationQueue.slice(0, 100); } renderGlobalNotifications(); updateGlobalNotifBadge(); // Show toast for important notifications if (type === 'error' || type === 'success') { showNotificationToast(notification); } } /** * Show a brief toast notification */ function showNotificationToast(notification) { const typeIcon = { 'info': 'â„šī¸', 'success': '✅', 'warning': 'âš ī¸', 'error': '❌' }[notification.type] || 'â„šī¸'; // Remove existing toast const existing = document.querySelector('.notif-toast'); if (existing) existing.remove(); const toast = document.createElement('div'); toast.className = `notif-toast type-${notification.type}`; toast.innerHTML = ` ${typeIcon} ${escapeHtml(notification.message)} `; document.body.appendChild(toast); // Animate in requestAnimationFrame(() => toast.classList.add('show')); // Auto-remove setTimeout(() => { toast.classList.remove('show'); setTimeout(() => toast.remove(), 300); }, 3000); } /** * Render notification list */ function renderGlobalNotifications() { const listEl = document.getElementById('globalNotificationList'); if (!listEl) return; if (globalNotificationQueue.length === 0) { listEl.innerHTML = `
No notifications

System events and task updates will appear here

`; return; } listEl.innerHTML = globalNotificationQueue.map(notif => { const typeIcon = { 'info': 'â„šī¸', 'success': '✅', 'warning': 'âš ī¸', 'error': '❌' }[notif.type] || 'â„šī¸'; const time = formatNotifTime(notif.timestamp); const sourceLabel = notif.source ? `${notif.source}` : ''; return `
${typeIcon} ${escapeHtml(notif.message)} ${sourceLabel}
${notif.details ? `
${escapeHtml(notif.details)}
` : ''}
${time}
`; }).join(''); } /** * Format notification time */ function formatNotifTime(timestamp) { const date = new Date(timestamp); const now = new Date(); const diff = now - date; if (diff < 60000) return 'Just now'; if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`; if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`; return date.toLocaleDateString(); } /** * Update notification badge */ function updateGlobalNotifBadge() { const badge = document.getElementById('globalNotifBadge'); if (badge) { const unreadCount = globalNotificationQueue.filter(n => !n.read).length; badge.textContent = unreadCount; badge.style.display = unreadCount > 0 ? 'flex' : 'none'; } } /** * Clear all notifications */ function clearGlobalNotifications() { globalNotificationQueue = []; renderGlobalNotifications(); updateGlobalNotifBadge(); } /** * Mark all as read */ function markAllNotificationsRead() { globalNotificationQueue.forEach(n => n.read = true); renderGlobalNotifications(); updateGlobalNotifBadge(); }