diff --git a/src/web/public/app.js b/src/web/public/app.js index 51609a0d..0f801c15 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -414,6 +414,40 @@ class ClaudemanApp { dot.className = 'status-dot ' + status; text.textContent = status === 'connected' ? 'Connected' : 'Disconnected'; + + // Show toast on disconnect + if (status === 'disconnected') { + this.showToast('Connection lost. Reconnecting...', 'warning'); + } else if (status === 'connected') { + this.showToast('Connected to server', 'success'); + } + } + + // Show a toast notification + showToast(message, type = 'info') { + const toast = document.createElement('div'); + toast.className = `toast toast-${type}`; + toast.textContent = message; + + // Create container if needed + let container = document.querySelector('.toast-container'); + if (!container) { + container = document.createElement('div'); + container.className = 'toast-container'; + document.body.appendChild(container); + } + container.appendChild(toast); + + // Animate in + requestAnimationFrame(() => { + toast.classList.add('show'); + }); + + // Remove after delay + setTimeout(() => { + toast.classList.remove('show'); + setTimeout(() => toast.remove(), 300); + }, 3000); } handleInit(data) { diff --git a/src/web/public/styles.css b/src/web/public/styles.css index 6a726ac8..d95fc071 100644 --- a/src/web/public/styles.css +++ b/src/web/public/styles.css @@ -868,6 +868,57 @@ body { color: var(--green); } +/* Toast notifications */ +.toast-container { + position: fixed; + top: 1rem; + right: 1rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + z-index: 10000; + pointer-events: none; +} + +.toast { + background: linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%); + border: 1px solid var(--border); + border-radius: 8px; + padding: 0.875rem 1.25rem; + font-size: 0.875rem; + color: var(--text); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4); + transform: translateX(100%); + opacity: 0; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + pointer-events: auto; +} + +.toast.show { + transform: translateX(0); + opacity: 1; +} + +.toast-success { + border-color: rgba(34, 197, 94, 0.4); + background: linear-gradient(135deg, rgba(34, 197, 94, 0.2) 0%, rgba(34, 197, 94, 0.1) 100%); +} + +.toast-error { + border-color: rgba(239, 68, 68, 0.4); + background: linear-gradient(135deg, rgba(239, 68, 68, 0.2) 0%, rgba(239, 68, 68, 0.1) 100%); +} + +.toast-warning { + border-color: rgba(234, 179, 8, 0.4); + background: linear-gradient(135deg, rgba(234, 179, 8, 0.2) 0%, rgba(234, 179, 8, 0.1) 100%); +} + +.toast-info { + border-color: rgba(59, 130, 246, 0.4); + background: linear-gradient(135deg, rgba(59, 130, 246, 0.2) 0%, rgba(59, 130, 246, 0.1) 100%); +} + /* Tabs */ .tabs { display: flex;