claude-team-dashboard 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of claude-team-dashboard might be problematic. Click here for more details.

Files changed (49) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/LICENSE +21 -0
  3. package/README.md +722 -0
  4. package/cleanup.js +73 -0
  5. package/config.js +50 -0
  6. package/dist/assets/icons-Ijf8rQIc.js +1 -0
  7. package/dist/assets/index-Cqc1m1x_.css +1 -0
  8. package/dist/assets/index-jGy3ms0W.js +9 -0
  9. package/dist/assets/react-vendor-DbmSkCAF.js +1 -0
  10. package/dist/index.html +16 -0
  11. package/index.html +13 -0
  12. package/package.json +93 -0
  13. package/server.js +953 -0
  14. package/src/App.jsx +372 -0
  15. package/src/animations-enhanced.css +929 -0
  16. package/src/animations.css +783 -0
  17. package/src/components/ActivityFeed.jsx +289 -0
  18. package/src/components/AgentActivity.jsx +104 -0
  19. package/src/components/AgentCard.jsx +163 -0
  20. package/src/components/AgentOutputViewer.jsx +334 -0
  21. package/src/components/ArchiveViewer.jsx +283 -0
  22. package/src/components/ConnectionStatus.jsx +124 -0
  23. package/src/components/DetailedTaskProgress.jsx +126 -0
  24. package/src/components/ErrorBoundary.jsx +132 -0
  25. package/src/components/Header.jsx +154 -0
  26. package/src/components/LiveAgentStream.jsx +176 -0
  27. package/src/components/LiveCommunication.jsx +326 -0
  28. package/src/components/LiveMetrics.jsx +100 -0
  29. package/src/components/RealTimeMessages.jsx +298 -0
  30. package/src/components/SkeletonLoader.jsx +384 -0
  31. package/src/components/StatsOverview.jsx +209 -0
  32. package/src/components/SystemStatus.jsx +57 -0
  33. package/src/components/TaskList.jsx +306 -0
  34. package/src/components/TeamCard.jsx +126 -0
  35. package/src/components/TeamHistory.jsx +204 -0
  36. package/src/components/__tests__/ConnectionStatus.test.jsx +54 -0
  37. package/src/components/__tests__/StatsOverview.test.jsx +66 -0
  38. package/src/config/constants.js +59 -0
  39. package/src/hooks/useCounterAnimation.js +219 -0
  40. package/src/hooks/useWebSocket.js +76 -0
  41. package/src/index.css +1818 -0
  42. package/src/main.jsx +17 -0
  43. package/src/polish-enhancements.css +303 -0
  44. package/src/premium-visual-polish.css +830 -0
  45. package/src/responsive-enhancements.css +666 -0
  46. package/src/styles/theme.css +395 -0
  47. package/src/test/setup.js +19 -0
  48. package/start.js +36 -0
  49. package/vite.config.js +37 -0
@@ -0,0 +1,124 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { WifiOff, RefreshCw, Zap } from 'lucide-react';
4
+
5
+ export function ConnectionStatus({ isConnected, error }) {
6
+ if (isConnected) {
7
+ return (
8
+ <div
9
+ className="inline-flex items-center gap-2.5 px-4 py-2 rounded-xl font-semibold text-sm transition-all duration-300 group"
10
+ role="status"
11
+ aria-live="polite"
12
+ aria-label="Connection status: Connected"
13
+ style={{
14
+ background: 'linear-gradient(135deg, rgba(34, 197, 94, 0.25) 0%, rgba(21, 128, 61, 0.15) 100%)',
15
+ color: '#4ade80',
16
+ border: '1px solid rgba(34, 197, 94, 0.4)',
17
+ boxShadow: '0 4px 12px rgba(34, 197, 94, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1)',
18
+ textShadow: '0 0 10px rgba(34, 197, 94, 0.4)'
19
+ }}
20
+ >
21
+ {/* Animated Pulse Indicator */}
22
+ <div className="relative">
23
+ <Zap
24
+ className="h-4 w-4 group-hover:scale-110 transition-transform duration-300"
25
+ style={{
26
+ filter: 'drop-shadow(0 0 6px rgba(34, 197, 94, 0.6))'
27
+ }}
28
+ />
29
+ <div
30
+ className="absolute inset-0 rounded-full animate-ping"
31
+ style={{
32
+ background: 'rgba(34, 197, 94, 0.4)'
33
+ }}
34
+ />
35
+ </div>
36
+
37
+ <span className="tracking-wide">Connected</span>
38
+
39
+ {/* Live Indicator Dot */}
40
+ <div
41
+ className="h-2 w-2 rounded-full animate-pulse"
42
+ style={{
43
+ background: '#4ade80',
44
+ boxShadow: '0 0 8px rgba(34, 197, 94, 0.8), 0 0 16px rgba(34, 197, 94, 0.4)'
45
+ }}
46
+ />
47
+ </div>
48
+ );
49
+ }
50
+
51
+ if (error) {
52
+ return (
53
+ <div
54
+ className="inline-flex items-center gap-2.5 px-4 py-2 rounded-xl font-semibold text-sm transition-all duration-300"
55
+ role="alert"
56
+ aria-live="assertive"
57
+ aria-label={`Connection error: ${error}`}
58
+ style={{
59
+ background: 'linear-gradient(135deg, rgba(239, 68, 68, 0.25) 0%, rgba(220, 38, 38, 0.15) 100%)',
60
+ color: '#f87171',
61
+ border: '1px solid rgba(239, 68, 68, 0.4)',
62
+ boxShadow: '0 4px 12px rgba(239, 68, 68, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1)',
63
+ textShadow: '0 0 10px rgba(239, 68, 68, 0.4)',
64
+ animation: 'shake 0.5s ease-in-out'
65
+ }}
66
+ >
67
+ <WifiOff
68
+ className="h-4 w-4"
69
+ style={{
70
+ filter: 'drop-shadow(0 0 6px rgba(239, 68, 68, 0.6))'
71
+ }}
72
+ />
73
+ <span className="tracking-wide truncate max-w-xs">{error}</span>
74
+ </div>
75
+ );
76
+ }
77
+
78
+ return (
79
+ <div
80
+ className="inline-flex items-center gap-2.5 px-4 py-2 rounded-xl font-semibold text-sm transition-all duration-300"
81
+ role="status"
82
+ aria-live="polite"
83
+ aria-label="Connection status: Connecting"
84
+ aria-busy="true"
85
+ style={{
86
+ background: 'linear-gradient(135deg, rgba(234, 179, 8, 0.25) 0%, rgba(202, 138, 4, 0.15) 100%)',
87
+ color: '#facc15',
88
+ border: '1px solid rgba(234, 179, 8, 0.4)',
89
+ boxShadow: '0 4px 12px rgba(234, 179, 8, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1)',
90
+ textShadow: '0 0 10px rgba(234, 179, 8, 0.4)'
91
+ }}
92
+ >
93
+ <RefreshCw
94
+ className="h-4 w-4 animate-spin"
95
+ style={{
96
+ filter: 'drop-shadow(0 0 6px rgba(234, 179, 8, 0.6))',
97
+ animationDuration: '2s'
98
+ }}
99
+ />
100
+ <span className="tracking-wide">Connecting...</span>
101
+
102
+ {/* Pulsing Dots */}
103
+ <div className="flex gap-1">
104
+ {[0, 1, 2].map((i) => (
105
+ <div
106
+ key={i}
107
+ className="h-1.5 w-1.5 rounded-full"
108
+ style={{
109
+ background: '#facc15',
110
+ animation: 'pulse 1.4s ease-in-out infinite',
111
+ animationDelay: `${i * 0.2}s`,
112
+ boxShadow: '0 0 6px rgba(234, 179, 8, 0.6)'
113
+ }}
114
+ />
115
+ ))}
116
+ </div>
117
+ </div>
118
+ );
119
+ }
120
+
121
+ ConnectionStatus.propTypes = {
122
+ isConnected: PropTypes.bool.isRequired,
123
+ error: PropTypes.string
124
+ };
@@ -0,0 +1,126 @@
1
+ import React from 'react';
2
+ import { CheckCircle, Clock, AlertCircle, TrendingUp } from 'lucide-react';
3
+
4
+ export function DetailedTaskProgress({ tasks }) {
5
+ if (!tasks || tasks.length === 0) return null;
6
+
7
+ const pendingTasks = tasks.filter(t => t.status === 'pending');
8
+ const inProgressTasks = tasks.filter(t => t.status === 'in_progress');
9
+ const completedTasks = tasks.filter(t => t.status === 'completed');
10
+ const blockedTasks = tasks.filter(t => t.blockedBy && t.blockedBy.length > 0);
11
+
12
+ const completionPercentage = tasks.length > 0
13
+ ? Math.round((completedTasks.length / tasks.length) * 100)
14
+ : 0;
15
+
16
+ return (
17
+ <div className="card">
18
+ <div className="flex items-center justify-between mb-4">
19
+ <div className="flex items-center gap-2">
20
+ <TrendingUp className="h-5 w-5 text-claude-orange" />
21
+ <h3 className="text-lg font-semibold text-white">Task Progress Details</h3>
22
+ </div>
23
+ <span className="text-2xl font-bold text-white">{completionPercentage}%</span>
24
+ </div>
25
+
26
+ {/* Overall Progress Bar */}
27
+ <div className="mb-6">
28
+ <div className="flex items-center justify-between mb-2">
29
+ <span className="text-sm text-gray-400">Overall Completion</span>
30
+ <span className="text-sm text-white font-semibold">
31
+ {completedTasks.length} / {tasks.length} tasks
32
+ </span>
33
+ </div>
34
+ <div className="w-full bg-gray-700 rounded-full h-3 overflow-hidden">
35
+ <div
36
+ className="h-3 rounded-full transition-all duration-500"
37
+ style={{
38
+ width: `${completionPercentage}%`,
39
+ background: 'linear-gradient(90deg, #10b981 0%, #34d399 50%, #6ee7b7 100%)',
40
+ boxShadow: '0 0 10px rgba(16, 185, 129, 0.5)'
41
+ }}
42
+ ></div>
43
+ </div>
44
+ </div>
45
+
46
+ {/* Detailed Breakdown */}
47
+ <div className="space-y-3">
48
+ {/* Pending */}
49
+ <div className="flex items-center justify-between p-3 rounded-lg bg-yellow-500/10 border border-yellow-500/30">
50
+ <div className="flex items-center gap-3">
51
+ <div className="p-2 rounded-lg bg-yellow-500/20">
52
+ <Clock className="h-4 w-4 text-yellow-400" />
53
+ </div>
54
+ <div>
55
+ <div className="text-sm font-semibold text-white">Pending Tasks</div>
56
+ <div className="text-xs text-gray-400">Waiting to start</div>
57
+ </div>
58
+ </div>
59
+ <div className="text-right">
60
+ <div className="text-xl font-bold text-yellow-400">{pendingTasks.length}</div>
61
+ <div className="text-xs text-gray-400">
62
+ {tasks.length > 0 ? Math.round((pendingTasks.length / tasks.length) * 100) : 0}%
63
+ </div>
64
+ </div>
65
+ </div>
66
+
67
+ {/* In Progress */}
68
+ <div className="flex items-center justify-between p-3 rounded-lg bg-blue-500/10 border border-blue-500/30">
69
+ <div className="flex items-center gap-3">
70
+ <div className="p-2 rounded-lg bg-blue-500/20">
71
+ <div className="h-4 w-4 border-2 border-blue-400 border-t-transparent rounded-full animate-spin"></div>
72
+ </div>
73
+ <div>
74
+ <div className="text-sm font-semibold text-white">In Progress</div>
75
+ <div className="text-xs text-gray-400">Currently working</div>
76
+ </div>
77
+ </div>
78
+ <div className="text-right">
79
+ <div className="text-xl font-bold text-blue-400">{inProgressTasks.length}</div>
80
+ <div className="text-xs text-gray-400">
81
+ {tasks.length > 0 ? Math.round((inProgressTasks.length / tasks.length) * 100) : 0}%
82
+ </div>
83
+ </div>
84
+ </div>
85
+
86
+ {/* Completed */}
87
+ <div className="flex items-center justify-between p-3 rounded-lg bg-green-500/10 border border-green-500/30">
88
+ <div className="flex items-center gap-3">
89
+ <div className="p-2 rounded-lg bg-green-500/20">
90
+ <CheckCircle className="h-4 w-4 text-green-400" />
91
+ </div>
92
+ <div>
93
+ <div className="text-sm font-semibold text-white">Completed</div>
94
+ <div className="text-xs text-gray-400">Successfully finished</div>
95
+ </div>
96
+ </div>
97
+ <div className="text-right">
98
+ <div className="text-xl font-bold text-green-400">{completedTasks.length}</div>
99
+ <div className="text-xs text-gray-400">{completionPercentage}%</div>
100
+ </div>
101
+ </div>
102
+
103
+ {/* Blocked */}
104
+ {blockedTasks.length > 0 && (
105
+ <div className="flex items-center justify-between p-3 rounded-lg bg-red-500/10 border border-red-500/30">
106
+ <div className="flex items-center gap-3">
107
+ <div className="p-2 rounded-lg bg-red-500/20">
108
+ <AlertCircle className="h-4 w-4 text-red-400" />
109
+ </div>
110
+ <div>
111
+ <div className="text-sm font-semibold text-white">Blocked</div>
112
+ <div className="text-xs text-gray-400">Waiting on dependencies</div>
113
+ </div>
114
+ </div>
115
+ <div className="text-right">
116
+ <div className="text-xl font-bold text-red-400">{blockedTasks.length}</div>
117
+ <div className="text-xs text-gray-400">
118
+ {tasks.length > 0 ? Math.round((blockedTasks.length / tasks.length) * 100) : 0}%
119
+ </div>
120
+ </div>
121
+ </div>
122
+ )}
123
+ </div>
124
+ </div>
125
+ );
126
+ }
@@ -0,0 +1,132 @@
1
+ import React from 'react';
2
+ import { Activity, AlertCircle, RefreshCw } from 'lucide-react';
3
+
4
+ export class ErrorBoundary extends React.Component {
5
+ constructor(props) {
6
+ super(props);
7
+ this.state = {
8
+ hasError: false,
9
+ error: null,
10
+ errorInfo: null
11
+ };
12
+ }
13
+
14
+ static getDerivedStateFromError(error) {
15
+ return { hasError: true };
16
+ }
17
+
18
+ componentDidCatch(error, errorInfo) {
19
+ console.error('Error caught by boundary:', error, errorInfo);
20
+ this.setState({
21
+ error,
22
+ errorInfo
23
+ });
24
+ }
25
+
26
+ handleReload = () => {
27
+ window.location.reload();
28
+ };
29
+
30
+ handleReset = () => {
31
+ this.setState({
32
+ hasError: false,
33
+ error: null,
34
+ errorInfo: null
35
+ });
36
+ };
37
+
38
+ render() {
39
+ if (this.state.hasError) {
40
+ return (
41
+ <div className="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900 flex items-center justify-center p-6">
42
+ <div className="max-w-2xl w-full">
43
+ {/* Error Card */}
44
+ <div className="card p-8 text-center">
45
+ {/* Icon */}
46
+ <div className="mb-6 flex justify-center">
47
+ <div className="relative">
48
+ <Activity className="h-20 w-20 text-claude-orange animate-pulse" />
49
+ <AlertCircle className="h-8 w-8 text-red-400 absolute -top-2 -right-2" />
50
+ </div>
51
+ </div>
52
+
53
+ {/* Title */}
54
+ <h1 className="text-3xl font-bold text-white mb-3">
55
+ Oops! Something went wrong
56
+ </h1>
57
+
58
+ {/* Description */}
59
+ <p className="text-gray-400 mb-6">
60
+ The dashboard encountered an unexpected error. Don't worry, your data is safe.
61
+ </p>
62
+
63
+ {/* Error Details (Development) */}
64
+ {process.env.NODE_ENV === 'development' && this.state.error && (
65
+ <div className="mb-6 text-left bg-gray-800 rounded-lg p-4 border border-red-500/30">
66
+ <h3 className="text-sm font-semibold text-red-400 mb-2">Error Details:</h3>
67
+ <pre className="text-xs text-gray-300 overflow-x-auto">
68
+ {this.state.error.toString()}
69
+ </pre>
70
+ {this.state.errorInfo && (
71
+ <details className="mt-3">
72
+ <summary className="text-xs text-gray-400 cursor-pointer hover:text-white">
73
+ Component Stack
74
+ </summary>
75
+ <pre className="text-xs text-gray-300 mt-2 overflow-x-auto">
76
+ {this.state.errorInfo.componentStack}
77
+ </pre>
78
+ </details>
79
+ )}
80
+ </div>
81
+ )}
82
+
83
+ {/* Action Buttons */}
84
+ <div className="flex gap-4 justify-center">
85
+ <button
86
+ onClick={this.handleReset}
87
+ className="flex items-center gap-2 px-6 py-3 bg-gray-700 hover:bg-gray-600 text-white rounded-lg font-medium transition-colors"
88
+ >
89
+ <RefreshCw className="h-4 w-4" />
90
+ Try Again
91
+ </button>
92
+ <button
93
+ onClick={this.handleReload}
94
+ className="flex items-center gap-2 px-6 py-3 bg-claude-orange hover:bg-orange-600 text-white rounded-lg font-medium transition-colors"
95
+ >
96
+ <RefreshCw className="h-4 w-4" />
97
+ Reload Dashboard
98
+ </button>
99
+ </div>
100
+
101
+ {/* Help Text */}
102
+ <p className="text-sm text-gray-500 mt-6">
103
+ If this error persists, please{' '}
104
+ <a
105
+ href="https://github.com/mukul975/agentdashboard/issues"
106
+ target="_blank"
107
+ rel="noopener noreferrer"
108
+ className="text-claude-orange hover:text-orange-400 underline"
109
+ >
110
+ report it on GitHub
111
+ </a>
112
+ </p>
113
+ </div>
114
+
115
+ {/* Tips Card */}
116
+ <div className="mt-6 card p-6">
117
+ <h3 className="text-lg font-semibold text-white mb-3">Troubleshooting Tips:</h3>
118
+ <ul className="text-sm text-gray-400 space-y-2">
119
+ <li>• Check if the backend server is running on port 3001</li>
120
+ <li>• Verify that Claude Code agent teams are active</li>
121
+ <li>• Try clearing your browser cache and reloading</li>
122
+ <li>• Check the browser console for additional error details</li>
123
+ </ul>
124
+ </div>
125
+ </div>
126
+ </div>
127
+ );
128
+ }
129
+
130
+ return this.props.children;
131
+ }
132
+ }
@@ -0,0 +1,154 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Activity, ExternalLink, Menu, X } from 'lucide-react';
4
+ import { ConnectionStatus } from './ConnectionStatus';
5
+
6
+ export function Header({ isConnected, error, onMenuToggle, isMenuOpen }) {
7
+ return (
8
+ <header
9
+ className="sticky top-0 z-50 border-b backdrop-blur-xl"
10
+ style={{
11
+ background: 'linear-gradient(135deg, rgba(17, 24, 39, 0.85) 0%, rgba(31, 41, 55, 0.9) 100%)',
12
+ borderColor: 'rgba(249, 115, 22, 0.15)',
13
+ boxShadow: '0 4px 24px rgba(0, 0, 0, 0.3), inset 0 -1px 0 rgba(249, 115, 22, 0.1)'
14
+ }}
15
+ role="banner"
16
+ >
17
+ <div className="container mx-auto px-6 py-4">
18
+ <div className="flex items-center justify-between">
19
+ {/* Logo and Title */}
20
+ <div className="flex items-center gap-4">
21
+ {/* Mobile Menu Button */}
22
+ <button
23
+ onClick={onMenuToggle}
24
+ className="lg:hidden p-2 rounded-lg transition-all duration-200 hover:bg-gray-700/50"
25
+ aria-label={isMenuOpen ? "Close menu" : "Open menu"}
26
+ >
27
+ {isMenuOpen ? (
28
+ <X className="h-6 w-6 text-gray-300" />
29
+ ) : (
30
+ <Menu className="h-6 w-6 text-gray-300" />
31
+ )}
32
+ </button>
33
+
34
+ {/* Logo with Gradient Animation */}
35
+ <div
36
+ className="relative p-3 rounded-xl overflow-hidden group"
37
+ style={{
38
+ background: 'linear-gradient(135deg, rgba(249, 115, 22, 0.2) 0%, rgba(251, 146, 60, 0.15) 100%)',
39
+ boxShadow: '0 4px 12px rgba(249, 115, 22, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.1)',
40
+ border: '1px solid rgba(249, 115, 22, 0.3)'
41
+ }}
42
+ >
43
+ {/* Animated Background Overlay */}
44
+ <div
45
+ className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-500"
46
+ style={{
47
+ background: 'linear-gradient(135deg, rgba(249, 115, 22, 0.3), rgba(251, 146, 60, 0.25))',
48
+ }}
49
+ />
50
+
51
+ <Activity
52
+ className="h-7 w-7 text-claude-orange relative z-10 group-hover:scale-110 transition-transform duration-300"
53
+ style={{
54
+ filter: 'drop-shadow(0 0 8px rgba(249, 115, 22, 0.5))'
55
+ }}
56
+ />
57
+
58
+ {/* Rotating Glow */}
59
+ <div
60
+ className="absolute inset-0 opacity-50"
61
+ style={{
62
+ background: 'radial-gradient(circle at center, rgba(249, 115, 22, 0.4), transparent 70%)',
63
+ animation: 'spin 20s linear infinite'
64
+ }}
65
+ />
66
+ </div>
67
+
68
+ {/* Title and Subtitle */}
69
+ <div>
70
+ <h1
71
+ className="text-2xl font-bold gradient-text"
72
+ style={{
73
+ background: 'linear-gradient(135deg, #fff 0%, #fb923c 100%)',
74
+ WebkitBackgroundClip: 'text',
75
+ WebkitTextFillColor: 'transparent',
76
+ backgroundClip: 'text',
77
+ letterSpacing: '-0.02em'
78
+ }}
79
+ >
80
+ Claude Agent Dashboard
81
+ </h1>
82
+ <p
83
+ className="text-sm mt-0.5"
84
+ style={{
85
+ color: 'rgba(209, 213, 219, 0.8)',
86
+ letterSpacing: '0.01em'
87
+ }}
88
+ >
89
+ Real-time agent team monitoring
90
+ </p>
91
+ </div>
92
+ </div>
93
+
94
+ {/* Right Side Actions */}
95
+ <div className="flex items-center gap-4">
96
+ {/* Connection Status */}
97
+ <ConnectionStatus isConnected={isConnected} error={error} />
98
+
99
+ {/* Documentation Link */}
100
+ <a
101
+ href="https://code.claude.com/docs/en/agent-teams"
102
+ target="_blank"
103
+ rel="noopener noreferrer"
104
+ className="hidden md:flex items-center gap-2 px-4 py-2 rounded-xl font-medium transition-all duration-300 group"
105
+ style={{
106
+ background: 'linear-gradient(135deg, rgba(55, 65, 81, 0.6) 0%, rgba(31, 41, 55, 0.6) 100%)',
107
+ border: '1px solid rgba(75, 85, 99, 0.5)',
108
+ color: '#e5e7eb',
109
+ boxShadow: '0 2px 8px rgba(0, 0, 0, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.05)'
110
+ }}
111
+ onMouseEnter={(e) => {
112
+ e.currentTarget.style.background = 'linear-gradient(135deg, rgba(249, 115, 22, 0.2) 0%, rgba(251, 146, 60, 0.15) 100%)';
113
+ e.currentTarget.style.borderColor = 'rgba(249, 115, 22, 0.4)';
114
+ e.currentTarget.style.transform = 'translateY(-2px)';
115
+ e.currentTarget.style.boxShadow = '0 4px 12px rgba(249, 115, 22, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1)';
116
+ }}
117
+ onMouseLeave={(e) => {
118
+ e.currentTarget.style.background = 'linear-gradient(135deg, rgba(55, 65, 81, 0.6) 0%, rgba(31, 41, 55, 0.6) 100%)';
119
+ e.currentTarget.style.borderColor = 'rgba(75, 85, 99, 0.5)';
120
+ e.currentTarget.style.transform = 'translateY(0)';
121
+ e.currentTarget.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.05)';
122
+ }}
123
+ >
124
+ <ExternalLink className="h-4 w-4 group-hover:rotate-12 transition-transform duration-300" />
125
+ <span className="text-sm">Documentation</span>
126
+ </a>
127
+ </div>
128
+ </div>
129
+ </div>
130
+
131
+ {/* Animated Bottom Border */}
132
+ <div
133
+ className="absolute bottom-0 left-0 right-0 h-px"
134
+ style={{
135
+ background: 'linear-gradient(90deg, transparent 0%, rgba(249, 115, 22, 0.5) 50%, transparent 100%)',
136
+ backgroundSize: '200% 100%',
137
+ animation: 'gradient-flow 3s ease infinite'
138
+ }}
139
+ />
140
+ </header>
141
+ );
142
+ }
143
+
144
+ Header.propTypes = {
145
+ isConnected: PropTypes.bool.isRequired,
146
+ error: PropTypes.string,
147
+ onMenuToggle: PropTypes.func,
148
+ isMenuOpen: PropTypes.bool
149
+ };
150
+
151
+ Header.defaultProps = {
152
+ onMenuToggle: () => {},
153
+ isMenuOpen: false
154
+ };