Phase 6: Terminal aesthetic refinements and notifications
Changes: - Added blinking terminal cursor animation - Smooth hover effects for execution/worker/workflow items - Hover animation: background highlight + border expand + slide - Loading pulse animation for loading states - Slide-in animation for log entries - Terminal beep sound using Web Audio API (different tones for success/error) - Real-time terminal notifications for command completion - Toast-style notifications with green glow effects - Auto-dismiss after 3 seconds with fade-out - Visual and audio feedback for user actions Sound features: - 800Hz tone for success (higher pitch) - 200Hz tone for errors (lower pitch) - 440Hz tone for info (standard A note) - 100ms duration, exponential fade-out - Graceful fallback if Web Audio API not supported Notification features: - Fixed position top-right - Terminal-themed styling with glow - Color-coded: green for success, red for errors - Icons: ✓ success, ✗ error, ℹ info - Smooth animations (slide-in, fade-out) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -690,6 +690,66 @@
|
|||||||
min-width: 120px;
|
min-width: 120px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Terminal Cursor Blink */
|
||||||
|
@keyframes cursor-blink {
|
||||||
|
0%, 49% { opacity: 1; }
|
||||||
|
50%, 100% { opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-cursor::after {
|
||||||
|
content: '▋';
|
||||||
|
animation: cursor-blink 1s step-end infinite;
|
||||||
|
color: var(--terminal-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover effects for execution items */
|
||||||
|
.execution-item {
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.execution-item:hover {
|
||||||
|
background: #001a00;
|
||||||
|
border-left-width: 5px;
|
||||||
|
transform: translateX(3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.worker-item:hover {
|
||||||
|
background: #001a00;
|
||||||
|
border-left-width: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-item:hover {
|
||||||
|
background: #001a00;
|
||||||
|
border-left-width: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading pulse effect */
|
||||||
|
.loading {
|
||||||
|
animation: loading-pulse 1.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loading-pulse {
|
||||||
|
0%, 100% { opacity: 0.6; }
|
||||||
|
50% { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Success/Error message animations */
|
||||||
|
@keyframes slide-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry {
|
||||||
|
animation: slide-in 0.3s ease-out;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -1498,6 +1558,77 @@
|
|||||||
loadExecutions();
|
loadExecutions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Terminal beep sound (Web Audio API)
|
||||||
|
function terminalBeep(type = 'success') {
|
||||||
|
try {
|
||||||
|
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||||
|
const oscillator = audioContext.createOscillator();
|
||||||
|
const gainNode = audioContext.createGain();
|
||||||
|
|
||||||
|
oscillator.connect(gainNode);
|
||||||
|
gainNode.connect(audioContext.destination);
|
||||||
|
|
||||||
|
// Different tones for different events
|
||||||
|
if (type === 'success') {
|
||||||
|
oscillator.frequency.value = 800; // Higher pitch for success
|
||||||
|
} else if (type === 'error') {
|
||||||
|
oscillator.frequency.value = 200; // Lower pitch for errors
|
||||||
|
} else {
|
||||||
|
oscillator.frequency.value = 440; // Standard A note
|
||||||
|
}
|
||||||
|
|
||||||
|
oscillator.type = 'sine';
|
||||||
|
gainNode.gain.setValueAtTime(0.1, audioContext.currentTime);
|
||||||
|
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.1);
|
||||||
|
|
||||||
|
oscillator.start(audioContext.currentTime);
|
||||||
|
oscillator.stop(audioContext.currentTime + 0.1);
|
||||||
|
} catch (error) {
|
||||||
|
// Silently fail if Web Audio API not supported
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show terminal notification
|
||||||
|
function showTerminalNotification(message, type = 'info') {
|
||||||
|
const notification = document.createElement('div');
|
||||||
|
notification.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
top: 80px;
|
||||||
|
right: 20px;
|
||||||
|
background: #001a00;
|
||||||
|
border: 2px solid var(--terminal-green);
|
||||||
|
color: var(--terminal-green);
|
||||||
|
padding: 15px 20px;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
z-index: 10000;
|
||||||
|
animation: slide-in 0.3s ease-out;
|
||||||
|
box-shadow: 0 0 20px rgba(0, 255, 65, 0.3);
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (type === 'error') {
|
||||||
|
notification.style.borderColor = '#ff4444';
|
||||||
|
notification.style.color = '#ff4444';
|
||||||
|
message = '✗ ' + message;
|
||||||
|
} else if (type === 'success') {
|
||||||
|
message = '✓ ' + message;
|
||||||
|
} else {
|
||||||
|
message = 'ℹ ' + message;
|
||||||
|
}
|
||||||
|
|
||||||
|
notification.textContent = message;
|
||||||
|
document.body.appendChild(notification);
|
||||||
|
|
||||||
|
// Play beep
|
||||||
|
terminalBeep(type);
|
||||||
|
|
||||||
|
// Remove after 3 seconds
|
||||||
|
setTimeout(() => {
|
||||||
|
notification.style.opacity = '0';
|
||||||
|
notification.style.transition = 'opacity 0.5s';
|
||||||
|
setTimeout(() => notification.remove(), 500);
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
function connectWebSocket() {
|
function connectWebSocket() {
|
||||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
ws = new WebSocket(`${protocol}//${window.location.host}`);
|
ws = new WebSocket(`${protocol}//${window.location.host}`);
|
||||||
@@ -1516,6 +1647,13 @@
|
|||||||
console.log(`Error: ${data.stderr}`);
|
console.log(`Error: ${data.stderr}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show terminal notification
|
||||||
|
if (data.success) {
|
||||||
|
showTerminalNotification('Command completed successfully', 'success');
|
||||||
|
} else {
|
||||||
|
showTerminalNotification('Command execution failed', 'error');
|
||||||
|
}
|
||||||
|
|
||||||
// If viewing execution details, refresh that specific execution
|
// If viewing execution details, refresh that specific execution
|
||||||
const executionModal = document.getElementById('viewExecutionModal');
|
const executionModal = document.getElementById('viewExecutionModal');
|
||||||
if (executionModal && executionModal.classList.contains('show')) {
|
if (executionModal && executionModal.classList.contains('show')) {
|
||||||
|
|||||||
Reference in New Issue
Block a user