mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-11 02:33:51 +08:00
feat(dashboard): unify icons with Lucide Icons library
- Introduce Lucide Icons via CDN for consistent SVG icons - Replace emoji icons with Lucide SVG icons in sidebar navigation - Fix Sessions/Explorer icon confusion (📁/📂 → history/folder-tree) - Update top bar icons (logo, theme toggle, search, refresh) - Update stats section icons with colored Lucide icons - Add icon animations support (animate-spin for loading states) - Update Explorer view with Lucide folder/file icons - Support dark/light theme icon adaptation Icon mapping: - Explorer: folder-tree (was 📂) - Sessions: history (was 📁) - Overview: bar-chart-3 - Active: play-circle - Archived: archive - Lite Plan: file-edit - Lite Fix: wrench - MCP Servers: plug - Hooks: webhook
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
// ==========================================
|
||||
// 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 = `
|
||||
<div class="global-notif-fab" id="globalNotificationFab" onclick="toggleGlobalNotifications()" title="Notifications">
|
||||
<span class="fab-icon">🔔</span>
|
||||
<span class="fab-badge" id="globalNotifBadge">0</span>
|
||||
</div>
|
||||
|
||||
<div class="global-notif-panel" id="globalNotificationPanel">
|
||||
<div class="global-notif-header">
|
||||
<span class="global-notif-title">🔔 Notifications</span>
|
||||
<button class="global-notif-close" onclick="toggleGlobalNotifications()">×</button>
|
||||
</div>
|
||||
<div class="global-notif-actions">
|
||||
<button class="notif-action-btn" onclick="clearGlobalNotifications()">
|
||||
<span>🗑️</span> Clear All
|
||||
</button>
|
||||
</div>
|
||||
<div class="global-notif-list" id="globalNotificationList">
|
||||
<div class="global-notif-empty">
|
||||
<span>No notifications</span>
|
||||
<p>System events and task updates will appear here</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
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 = `
|
||||
<span class="toast-icon">${typeIcon}</span>
|
||||
<span class="toast-message">${escapeHtml(notification.message)}</span>
|
||||
`;
|
||||
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 = `
|
||||
<div class="global-notif-empty">
|
||||
<span>No notifications</span>
|
||||
<p>System events and task updates will appear here</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
listEl.innerHTML = globalNotificationQueue.map(notif => {
|
||||
const typeIcon = {
|
||||
'info': 'ℹ️',
|
||||
'success': '✅',
|
||||
'warning': '⚠️',
|
||||
'error': '❌'
|
||||
}[notif.type] || 'ℹ️';
|
||||
|
||||
const time = formatNotifTime(notif.timestamp);
|
||||
const sourceLabel = notif.source ? `<span class="notif-source">${notif.source}</span>` : '';
|
||||
|
||||
return `
|
||||
<div class="global-notif-item type-${notif.type} ${notif.read ? 'read' : ''}" data-id="${notif.id}">
|
||||
<div class="notif-item-header">
|
||||
<span class="notif-icon">${typeIcon}</span>
|
||||
<span class="notif-message">${escapeHtml(notif.message)}</span>
|
||||
${sourceLabel}
|
||||
</div>
|
||||
${notif.details ? `<div class="notif-details">${escapeHtml(notif.details)}</div>` : ''}
|
||||
<div class="notif-meta">
|
||||
<span class="notif-time">${time}</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).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();
|
||||
}
|
||||
|
||||
@@ -96,6 +96,8 @@ function initNavigation() {
|
||||
renderMcpManager();
|
||||
} else if (currentView === 'project-overview') {
|
||||
renderProjectOverview();
|
||||
} else if (currentView === 'explorer') {
|
||||
renderExplorer();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -112,6 +114,8 @@ function updateContentTitle() {
|
||||
titleEl.textContent = 'Project Overview';
|
||||
} else if (currentView === 'mcp-manager') {
|
||||
titleEl.textContent = 'MCP Server Management';
|
||||
} else if (currentView === 'explorer') {
|
||||
titleEl.textContent = 'File Explorer';
|
||||
} else if (currentView === 'liteTasks') {
|
||||
const names = { 'lite-plan': 'Lite Plan Sessions', 'lite-fix': 'Lite Fix Sessions' };
|
||||
titleEl.textContent = names[currentLiteType] || 'Lite Tasks';
|
||||
|
||||
@@ -6,6 +6,7 @@ function initTheme() {
|
||||
const saved = localStorage.getItem('theme') || 'light';
|
||||
document.documentElement.setAttribute('data-theme', saved);
|
||||
updateThemeIcon(saved);
|
||||
updateHljsTheme(saved);
|
||||
|
||||
document.getElementById('themeToggle').addEventListener('click', () => {
|
||||
const current = document.documentElement.getAttribute('data-theme');
|
||||
@@ -13,9 +14,36 @@ function initTheme() {
|
||||
document.documentElement.setAttribute('data-theme', next);
|
||||
localStorage.setItem('theme', next);
|
||||
updateThemeIcon(next);
|
||||
updateHljsTheme(next);
|
||||
});
|
||||
}
|
||||
|
||||
function updateThemeIcon(theme) {
|
||||
document.getElementById('themeToggle').textContent = theme === 'light' ? '🌙' : '☀️';
|
||||
const darkIcon = document.querySelector('.theme-icon-dark');
|
||||
const lightIcon = document.querySelector('.theme-icon-light');
|
||||
if (darkIcon && lightIcon) {
|
||||
if (theme === 'light') {
|
||||
darkIcon.classList.remove('hidden');
|
||||
lightIcon.classList.add('hidden');
|
||||
} else {
|
||||
darkIcon.classList.add('hidden');
|
||||
lightIcon.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateHljsTheme(theme) {
|
||||
// Toggle highlight.js theme stylesheets
|
||||
const darkTheme = document.getElementById('hljs-theme-dark');
|
||||
const lightTheme = document.getElementById('hljs-theme-light');
|
||||
|
||||
if (darkTheme && lightTheme) {
|
||||
if (theme === 'dark') {
|
||||
darkTheme.disabled = false;
|
||||
lightTheme.disabled = true;
|
||||
} else {
|
||||
darkTheme.disabled = true;
|
||||
lightTheme.disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user