2026-01-08 22:49:48 -05:00
|
|
|
|
/**
|
2026-01-09 17:00:35 -05:00
|
|
|
|
* Terminal-style toast notification system with queuing
|
2026-01-08 22:49:48 -05:00
|
|
|
|
*/
|
|
|
|
|
|
|
2026-01-09 17:00:35 -05:00
|
|
|
|
// Toast queue management
|
|
|
|
|
|
let toastQueue = [];
|
|
|
|
|
|
let currentToast = null;
|
|
|
|
|
|
|
2026-01-08 22:49:48 -05:00
|
|
|
|
function showToast(message, type = 'info', duration = 3000) {
|
2026-01-09 17:00:35 -05:00
|
|
|
|
// Queue if a toast is already showing
|
|
|
|
|
|
if (currentToast) {
|
|
|
|
|
|
toastQueue.push({ message, type, duration });
|
|
|
|
|
|
return;
|
2026-01-08 22:49:48 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-09 17:00:35 -05:00
|
|
|
|
displayToast(message, type, duration);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function displayToast(message, type, duration) {
|
2026-01-08 22:49:48 -05:00
|
|
|
|
// Create toast element
|
|
|
|
|
|
const toast = document.createElement('div');
|
|
|
|
|
|
toast.className = `terminal-toast toast-${type}`;
|
2026-01-09 17:00:35 -05:00
|
|
|
|
currentToast = toast;
|
2026-01-08 22:49:48 -05:00
|
|
|
|
|
|
|
|
|
|
// Icon based on type
|
|
|
|
|
|
const icons = {
|
|
|
|
|
|
success: '✓',
|
|
|
|
|
|
error: '✗',
|
|
|
|
|
|
info: 'ℹ',
|
|
|
|
|
|
warning: '⚠'
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
toast.innerHTML = `
|
|
|
|
|
|
<span class="toast-icon">[${icons[type] || 'ℹ'}]</span>
|
|
|
|
|
|
<span class="toast-message">${message}</span>
|
2026-01-09 17:00:35 -05:00
|
|
|
|
<span class="toast-close" style="margin-left: auto; cursor: pointer; opacity: 0.7; padding-left: 1rem;">[×]</span>
|
2026-01-08 22:49:48 -05:00
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
// Add to document
|
|
|
|
|
|
document.body.appendChild(toast);
|
|
|
|
|
|
|
|
|
|
|
|
// Trigger animation
|
|
|
|
|
|
setTimeout(() => toast.classList.add('show'), 10);
|
|
|
|
|
|
|
2026-01-09 17:00:35 -05:00
|
|
|
|
// Manual dismiss handler
|
|
|
|
|
|
const closeBtn = toast.querySelector('.toast-close');
|
|
|
|
|
|
closeBtn.addEventListener('click', () => dismissToast(toast));
|
|
|
|
|
|
|
2026-01-08 22:49:48 -05:00
|
|
|
|
// Auto-remove after duration
|
2026-01-09 17:00:35 -05:00
|
|
|
|
const timeoutId = setTimeout(() => {
|
|
|
|
|
|
dismissToast(toast);
|
2026-01-08 22:49:48 -05:00
|
|
|
|
}, duration);
|
2026-01-09 17:00:35 -05:00
|
|
|
|
|
|
|
|
|
|
// Store timeout ID for manual dismiss
|
|
|
|
|
|
toast.timeoutId = timeoutId;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function dismissToast(toast) {
|
|
|
|
|
|
// Clear auto-dismiss timeout
|
|
|
|
|
|
if (toast.timeoutId) {
|
|
|
|
|
|
clearTimeout(toast.timeoutId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
toast.classList.remove('show');
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
toast.remove();
|
|
|
|
|
|
currentToast = null;
|
|
|
|
|
|
|
|
|
|
|
|
// Show next toast in queue
|
|
|
|
|
|
if (toastQueue.length > 0) {
|
|
|
|
|
|
const next = toastQueue.shift();
|
|
|
|
|
|
displayToast(next.message, next.type, next.duration);
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 300);
|
2026-01-08 22:49:48 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Convenience functions
|
|
|
|
|
|
window.toast = {
|
|
|
|
|
|
success: (msg, duration) => showToast(msg, 'success', duration),
|
|
|
|
|
|
error: (msg, duration) => showToast(msg, 'error', duration),
|
|
|
|
|
|
info: (msg, duration) => showToast(msg, 'info', duration),
|
|
|
|
|
|
warning: (msg, duration) => showToast(msg, 'warning', duration)
|
|
|
|
|
|
};
|