claude-mpm 4.1.5__py3-none-any.whl → 4.1.7__py3-none-any.whl
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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/templates/agent-manager.json +1 -1
- claude_mpm/agents/templates/agent-manager.md +111 -34
- claude_mpm/agents/templates/research.json +39 -13
- claude_mpm/cli/__init__.py +2 -0
- claude_mpm/cli/commands/__init__.py +2 -0
- claude_mpm/cli/commands/configure.py +1221 -0
- claude_mpm/cli/commands/configure_tui.py +1921 -0
- claude_mpm/cli/parsers/base_parser.py +7 -0
- claude_mpm/cli/parsers/configure_parser.py +119 -0
- claude_mpm/cli/startup_logging.py +39 -12
- claude_mpm/config/socketio_config.py +33 -4
- claude_mpm/constants.py +1 -0
- claude_mpm/core/socketio_pool.py +35 -3
- claude_mpm/dashboard/static/css/connection-status.css +370 -0
- claude_mpm/dashboard/static/js/components/connection-debug.js +654 -0
- claude_mpm/dashboard/static/js/connection-manager.js +536 -0
- claude_mpm/dashboard/static/js/socket-client.js +40 -16
- claude_mpm/dashboard/templates/index.html +11 -0
- claude_mpm/hooks/claude_hooks/services/__init__.py +3 -1
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +17 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +190 -0
- claude_mpm/services/diagnostics/checks/__init__.py +2 -0
- claude_mpm/services/diagnostics/checks/instructions_check.py +418 -0
- claude_mpm/services/diagnostics/diagnostic_runner.py +15 -2
- claude_mpm/services/event_bus/direct_relay.py +230 -0
- claude_mpm/services/socketio/handlers/connection_handler.py +330 -0
- claude_mpm/services/socketio/server/broadcaster.py +32 -1
- claude_mpm/services/socketio/server/connection_manager.py +547 -0
- claude_mpm/services/socketio/server/core.py +78 -7
- claude_mpm/services/socketio/server/eventbus_integration.py +20 -9
- claude_mpm/services/socketio/server/main.py +74 -19
- {claude_mpm-4.1.5.dist-info → claude_mpm-4.1.7.dist-info}/METADATA +3 -1
- {claude_mpm-4.1.5.dist-info → claude_mpm-4.1.7.dist-info}/RECORD +38 -41
- claude_mpm/agents/OUTPUT_STYLE.md +0 -73
- claude_mpm/agents/backups/INSTRUCTIONS.md +0 -352
- claude_mpm/agents/templates/OPTIMIZATION_REPORT.md +0 -156
- claude_mpm/agents/templates/backup/data_engineer_agent_20250726_234551.json +0 -79
- claude_mpm/agents/templates/backup/documentation_agent_20250726_234551.json +0 -68
- claude_mpm/agents/templates/backup/engineer_agent_20250726_234551.json +0 -77
- claude_mpm/agents/templates/backup/ops_agent_20250726_234551.json +0 -78
- claude_mpm/agents/templates/backup/qa_agent_20250726_234551.json +0 -67
- claude_mpm/agents/templates/backup/research_agent_2025011_234551.json +0 -88
- claude_mpm/agents/templates/backup/research_agent_20250726_234551.json +0 -72
- claude_mpm/agents/templates/backup/research_memory_efficient.json +0 -88
- claude_mpm/agents/templates/backup/security_agent_20250726_234551.json +0 -78
- claude_mpm/agents/templates/backup/version_control_agent_20250726_234551.json +0 -62
- claude_mpm/agents/templates/vercel_ops_instructions.md +0 -582
- {claude_mpm-4.1.5.dist-info → claude_mpm-4.1.7.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.5.dist-info → claude_mpm-4.1.7.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.5.dist-info → claude_mpm-4.1.7.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.5.dist-info → claude_mpm-4.1.7.dist-info}/top_level.txt +0 -0
|
@@ -331,6 +331,13 @@ def create_parser(
|
|
|
331
331
|
except ImportError:
|
|
332
332
|
pass
|
|
333
333
|
|
|
334
|
+
try:
|
|
335
|
+
from .configure_parser import add_configure_subparser
|
|
336
|
+
|
|
337
|
+
add_configure_subparser(subparsers)
|
|
338
|
+
except ImportError:
|
|
339
|
+
pass
|
|
340
|
+
|
|
334
341
|
# Import and add additional command parsers from commands module
|
|
335
342
|
try:
|
|
336
343
|
from ..commands.aggregate import add_aggregate_parser
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configure command parser for claude-mpm CLI.
|
|
3
|
+
|
|
4
|
+
WHY: This module contains all arguments specific to the interactive configuration
|
|
5
|
+
management interface, allowing users to enable/disable agents, edit templates,
|
|
6
|
+
and manage behavior files.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
|
|
11
|
+
from ...constants import CLICommands
|
|
12
|
+
from .base_parser import add_common_arguments
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def add_configure_subparser(subparsers) -> argparse.ArgumentParser:
|
|
16
|
+
"""
|
|
17
|
+
Add the configure subparser for interactive configuration management.
|
|
18
|
+
|
|
19
|
+
WHY: Users need an interactive way to manage agent configurations,
|
|
20
|
+
templates, and behavior files through a terminal-based interface.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
subparsers: The subparsers object from the main parser
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
The configured configure subparser
|
|
27
|
+
"""
|
|
28
|
+
# Configure command - interactive TUI configuration
|
|
29
|
+
configure_parser = subparsers.add_parser(
|
|
30
|
+
CLICommands.CONFIGURE.value,
|
|
31
|
+
help="Interactive terminal-based configuration interface for managing agents and behaviors",
|
|
32
|
+
description="Launch an interactive Rich-based TUI for configuring claude-mpm agents, templates, and behavior files",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Add common arguments
|
|
36
|
+
add_common_arguments(configure_parser)
|
|
37
|
+
|
|
38
|
+
# Configuration scope options
|
|
39
|
+
scope_group = configure_parser.add_argument_group("configuration scope")
|
|
40
|
+
scope_group.add_argument(
|
|
41
|
+
"--scope",
|
|
42
|
+
choices=["project", "user"],
|
|
43
|
+
default="project",
|
|
44
|
+
help="Configuration scope to manage (default: project)",
|
|
45
|
+
)
|
|
46
|
+
# Note: --project-dir is already defined in base_parser.py
|
|
47
|
+
|
|
48
|
+
# Direct navigation options (skip main menu)
|
|
49
|
+
nav_group = configure_parser.add_argument_group("direct navigation")
|
|
50
|
+
nav_group.add_argument(
|
|
51
|
+
"--agents", action="store_true", help="Jump directly to agent management"
|
|
52
|
+
)
|
|
53
|
+
nav_group.add_argument(
|
|
54
|
+
"--templates", action="store_true", help="Jump directly to template editing"
|
|
55
|
+
)
|
|
56
|
+
nav_group.add_argument(
|
|
57
|
+
"--behaviors",
|
|
58
|
+
action="store_true",
|
|
59
|
+
help="Jump directly to behavior file management",
|
|
60
|
+
)
|
|
61
|
+
nav_group.add_argument(
|
|
62
|
+
"--version-info",
|
|
63
|
+
action="store_true",
|
|
64
|
+
help="Display version information and exit",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Non-interactive options
|
|
68
|
+
noninteractive_group = configure_parser.add_argument_group(
|
|
69
|
+
"non-interactive options"
|
|
70
|
+
)
|
|
71
|
+
noninteractive_group.add_argument(
|
|
72
|
+
"--list-agents", action="store_true", help="List all available agents and exit"
|
|
73
|
+
)
|
|
74
|
+
noninteractive_group.add_argument(
|
|
75
|
+
"--enable-agent",
|
|
76
|
+
type=str,
|
|
77
|
+
metavar="AGENT_NAME",
|
|
78
|
+
help="Enable a specific agent and exit",
|
|
79
|
+
)
|
|
80
|
+
noninteractive_group.add_argument(
|
|
81
|
+
"--disable-agent",
|
|
82
|
+
type=str,
|
|
83
|
+
metavar="AGENT_NAME",
|
|
84
|
+
help="Disable a specific agent and exit",
|
|
85
|
+
)
|
|
86
|
+
noninteractive_group.add_argument(
|
|
87
|
+
"--export-config",
|
|
88
|
+
type=str,
|
|
89
|
+
metavar="FILE",
|
|
90
|
+
help="Export current configuration to a file",
|
|
91
|
+
)
|
|
92
|
+
noninteractive_group.add_argument(
|
|
93
|
+
"--import-config",
|
|
94
|
+
type=str,
|
|
95
|
+
metavar="FILE",
|
|
96
|
+
help="Import configuration from a file",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Display options
|
|
100
|
+
display_group = configure_parser.add_argument_group("display options")
|
|
101
|
+
display_group.add_argument(
|
|
102
|
+
"--no-colors", action="store_true", help="Disable colored output in the TUI"
|
|
103
|
+
)
|
|
104
|
+
display_group.add_argument(
|
|
105
|
+
"--compact", action="store_true", help="Use compact display mode"
|
|
106
|
+
)
|
|
107
|
+
display_group.add_argument(
|
|
108
|
+
"--force-rich",
|
|
109
|
+
action="store_true",
|
|
110
|
+
help="Force use of Rich menu interface instead of full-screen Textual TUI",
|
|
111
|
+
)
|
|
112
|
+
display_group.add_argument(
|
|
113
|
+
"--no-textual",
|
|
114
|
+
dest="use_textual",
|
|
115
|
+
action="store_false",
|
|
116
|
+
help="Disable Textual TUI and use Rich menu interface",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
return configure_parser
|
|
@@ -57,17 +57,34 @@ def log_memory_stats(logger=None, prefix="Memory Usage"):
|
|
|
57
57
|
rss_mb = memory_info.rss / (1024 * 1024)
|
|
58
58
|
vms_mb = memory_info.vms / (1024 * 1024)
|
|
59
59
|
|
|
60
|
-
#
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
60
|
+
# On macOS, VMS can report misleading values (400+ TB)
|
|
61
|
+
# Skip VMS reporting if it's unreasonably large
|
|
62
|
+
import platform
|
|
63
|
+
|
|
64
|
+
if platform.system() == "Darwin" and vms_mb > 100000: # > 100GB is suspicious
|
|
65
|
+
# Get percentage of system memory if available
|
|
66
|
+
try:
|
|
67
|
+
memory_percent = process.memory_percent()
|
|
68
|
+
logger.info(
|
|
69
|
+
f"{prefix}: RSS={rss_mb:.1f}MB, System={memory_percent:.1f}%"
|
|
70
|
+
)
|
|
71
|
+
return {"rss_mb": rss_mb, "vms_mb": None, "percent": memory_percent}
|
|
72
|
+
except:
|
|
73
|
+
logger.info(f"{prefix}: RSS={rss_mb:.1f}MB")
|
|
74
|
+
return {"rss_mb": rss_mb, "vms_mb": None, "percent": None}
|
|
75
|
+
else:
|
|
76
|
+
# Normal VMS reporting for non-macOS or reasonable values
|
|
77
|
+
# Get percentage of system memory if available
|
|
78
|
+
try:
|
|
79
|
+
memory_percent = process.memory_percent()
|
|
80
|
+
logger.info(
|
|
81
|
+
f"{prefix}: RSS={rss_mb:.1f}MB, VMS={vms_mb:.1f}MB, "
|
|
82
|
+
f"System={memory_percent:.1f}%"
|
|
83
|
+
)
|
|
84
|
+
return {"rss_mb": rss_mb, "vms_mb": vms_mb, "percent": memory_percent}
|
|
85
|
+
except:
|
|
86
|
+
logger.info(f"{prefix}: RSS={rss_mb:.1f}MB, VMS={vms_mb:.1f}MB")
|
|
87
|
+
return {"rss_mb": rss_mb, "vms_mb": vms_mb, "percent": None}
|
|
71
88
|
|
|
72
89
|
except Exception as e:
|
|
73
90
|
logger.debug(f"Failed to get memory info: {e}")
|
|
@@ -512,11 +529,21 @@ def setup_startup_logging(project_root: Optional[Path] = None) -> Path:
|
|
|
512
529
|
# Log initial memory usage
|
|
513
530
|
if PSUTIL_AVAILABLE:
|
|
514
531
|
try:
|
|
532
|
+
import platform
|
|
533
|
+
|
|
515
534
|
process = psutil.Process()
|
|
516
535
|
memory_info = process.memory_info()
|
|
517
536
|
rss_mb = memory_info.rss / (1024 * 1024)
|
|
518
537
|
vms_mb = memory_info.vms / (1024 * 1024)
|
|
519
|
-
|
|
538
|
+
|
|
539
|
+
# On macOS, VMS can report misleading values (400+ TB)
|
|
540
|
+
# Skip VMS reporting if it's unreasonably large
|
|
541
|
+
if (
|
|
542
|
+
platform.system() == "Darwin" and vms_mb > 100000
|
|
543
|
+
): # > 100GB is suspicious
|
|
544
|
+
logger.info(f"Initial Memory: RSS={rss_mb:.1f}MB")
|
|
545
|
+
else:
|
|
546
|
+
logger.info(f"Initial Memory: RSS={rss_mb:.1f}MB, VMS={vms_mb:.1f}MB")
|
|
520
547
|
except Exception as e:
|
|
521
548
|
logger.debug(f"Failed to get initial memory info: {e}")
|
|
522
549
|
|
|
@@ -10,6 +10,8 @@ WHY configuration management:
|
|
|
10
10
|
- Supports multiple deployment scenarios (local, PyPI, Docker, etc.)
|
|
11
11
|
- Provides environment-specific defaults
|
|
12
12
|
- Allows runtime configuration overrides
|
|
13
|
+
|
|
14
|
+
CRITICAL: Ping/pong settings MUST match between client and server to prevent disconnections!
|
|
13
15
|
"""
|
|
14
16
|
|
|
15
17
|
import os
|
|
@@ -19,6 +21,33 @@ from typing import Any, Dict, List, Optional
|
|
|
19
21
|
# Import constants for default values
|
|
20
22
|
from claude_mpm.core.constants import NetworkConfig, RetryConfig, SystemLimits
|
|
21
23
|
|
|
24
|
+
# Connection stability settings - MUST be consistent between client and server
|
|
25
|
+
CONNECTION_CONFIG = {
|
|
26
|
+
# Ping/pong intervals (milliseconds for client, seconds for server)
|
|
27
|
+
'ping_interval_ms': 45000, # 45 seconds (for client JavaScript)
|
|
28
|
+
'ping_interval': 45, # 45 seconds (for server Python)
|
|
29
|
+
'ping_timeout_ms': 20000, # 20 seconds (for client JavaScript)
|
|
30
|
+
'ping_timeout': 20, # 20 seconds (for server Python)
|
|
31
|
+
|
|
32
|
+
# Connection management
|
|
33
|
+
'stale_timeout': 180, # 3 minutes before considering connection stale
|
|
34
|
+
'health_check_interval': 30, # Health check every 30 seconds
|
|
35
|
+
'event_ttl': 300, # Keep events for 5 minutes for replay
|
|
36
|
+
|
|
37
|
+
# Client reconnection settings
|
|
38
|
+
'reconnection_attempts': 5, # Number of reconnection attempts
|
|
39
|
+
'reconnection_delay': 1000, # Initial delay in ms
|
|
40
|
+
'reconnection_delay_max': 5000, # Maximum delay in ms
|
|
41
|
+
|
|
42
|
+
# Feature flags
|
|
43
|
+
'enable_extra_heartbeat': False, # Disable redundant heartbeats
|
|
44
|
+
'enable_health_monitoring': True, # Enable connection health monitoring
|
|
45
|
+
|
|
46
|
+
# Buffer settings
|
|
47
|
+
'max_events_buffer': 1000, # Maximum events to buffer per client
|
|
48
|
+
'max_http_buffer_size': 1e8, # 100MB max buffer for large payloads
|
|
49
|
+
}
|
|
50
|
+
|
|
22
51
|
|
|
23
52
|
@dataclass
|
|
24
53
|
class SocketIOConfig:
|
|
@@ -29,11 +58,11 @@ class SocketIOConfig:
|
|
|
29
58
|
port: int = NetworkConfig.DEFAULT_DASHBOARD_PORT
|
|
30
59
|
server_id: Optional[str] = None
|
|
31
60
|
|
|
32
|
-
# Connection settings
|
|
61
|
+
# Connection settings - Use centralized config for consistency
|
|
33
62
|
cors_allowed_origins: str = "*" # Configure properly for production
|
|
34
|
-
ping_timeout: int =
|
|
35
|
-
ping_interval: int =
|
|
36
|
-
max_http_buffer_size: int =
|
|
63
|
+
ping_timeout: int = CONNECTION_CONFIG['ping_timeout'] # 20 seconds
|
|
64
|
+
ping_interval: int = CONNECTION_CONFIG['ping_interval'] # 45 seconds
|
|
65
|
+
max_http_buffer_size: int = int(CONNECTION_CONFIG['max_http_buffer_size'])
|
|
37
66
|
|
|
38
67
|
# Compatibility settings
|
|
39
68
|
min_client_version: str = "0.7.0"
|
claude_mpm/constants.py
CHANGED
claude_mpm/core/socketio_pool.py
CHANGED
|
@@ -592,7 +592,7 @@ class SocketIOConnectionPool:
|
|
|
592
592
|
|
|
593
593
|
# 2-second timeout for connection
|
|
594
594
|
await asyncio.wait_for(
|
|
595
|
-
client.connect(self.server_url,
|
|
595
|
+
client.connect(self.server_url, wait=True),
|
|
596
596
|
timeout=2.0,
|
|
597
597
|
)
|
|
598
598
|
|
|
@@ -734,8 +734,8 @@ class SocketIOConnectionPool:
|
|
|
734
734
|
def emit(self, event: str, data: Dict[str, Any]) -> bool:
|
|
735
735
|
"""Emit an event through the connection pool.
|
|
736
736
|
|
|
737
|
-
This method provides compatibility for the legacy emit() interface
|
|
738
|
-
|
|
737
|
+
This method provides compatibility for the legacy emit() interface.
|
|
738
|
+
For critical hook events, we use direct emission to avoid batching delays.
|
|
739
739
|
|
|
740
740
|
Args:
|
|
741
741
|
event: Event name (e.g., "claude_event")
|
|
@@ -747,10 +747,42 @@ class SocketIOConnectionPool:
|
|
|
747
747
|
if not SOCKETIO_AVAILABLE or not self._running:
|
|
748
748
|
return False
|
|
749
749
|
|
|
750
|
+
# For critical claude_event, use direct emission to avoid batching delays
|
|
751
|
+
if event == "claude_event":
|
|
752
|
+
return self._emit_direct(event, data)
|
|
753
|
+
|
|
750
754
|
# Map to the modern emit_event method using default namespace
|
|
751
755
|
self.emit_event("/", event, data)
|
|
752
756
|
return True
|
|
753
757
|
|
|
758
|
+
def _emit_direct(self, event: str, data: Dict[str, Any]) -> bool:
|
|
759
|
+
"""Emit an event directly without batching.
|
|
760
|
+
|
|
761
|
+
This is used for critical events that need immediate delivery.
|
|
762
|
+
"""
|
|
763
|
+
try:
|
|
764
|
+
# Create a synchronous client for direct emission
|
|
765
|
+
import socketio
|
|
766
|
+
|
|
767
|
+
client = socketio.Client(logger=False, engineio_logger=False)
|
|
768
|
+
|
|
769
|
+
# Quick connect, emit, and disconnect
|
|
770
|
+
client.connect(self.server_url, wait=True, wait_timeout=1.0)
|
|
771
|
+
client.emit(event, data)
|
|
772
|
+
client.disconnect()
|
|
773
|
+
|
|
774
|
+
# Update stats
|
|
775
|
+
for stats in self.connection_stats.values():
|
|
776
|
+
stats.events_sent += 1
|
|
777
|
+
break
|
|
778
|
+
|
|
779
|
+
return True
|
|
780
|
+
except Exception as e:
|
|
781
|
+
self.logger.debug(f"Direct emit failed: {e}")
|
|
782
|
+
# Fall back to batched emission
|
|
783
|
+
self.emit_event("/", event, data)
|
|
784
|
+
return True
|
|
785
|
+
|
|
754
786
|
def get_stats(self) -> Dict[str, Any]:
|
|
755
787
|
"""Get connection pool statistics."""
|
|
756
788
|
with self.pool_lock:
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced Connection Status Styles
|
|
3
|
+
* Provides visual feedback for connection state and quality
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* Connection Status Badge Enhancements */
|
|
7
|
+
.status-badge {
|
|
8
|
+
display: inline-flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
gap: 6px;
|
|
11
|
+
padding: 6px 12px;
|
|
12
|
+
border-radius: 20px;
|
|
13
|
+
font-size: 13px;
|
|
14
|
+
font-weight: 600;
|
|
15
|
+
transition: all 0.3s ease;
|
|
16
|
+
position: relative;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.status-badge span {
|
|
21
|
+
display: inline-block;
|
|
22
|
+
animation: pulse 2s infinite;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Connection States */
|
|
26
|
+
.status-connecting {
|
|
27
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
28
|
+
color: white;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.status-connecting span {
|
|
32
|
+
animation: spin 1s linear infinite;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.status-connected {
|
|
36
|
+
background: linear-gradient(135deg, #667eea 0%, #4299e1 100%);
|
|
37
|
+
color: white;
|
|
38
|
+
box-shadow: 0 2px 10px rgba(66, 153, 225, 0.3);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.status-connected span {
|
|
42
|
+
color: #68d391;
|
|
43
|
+
animation: pulse-connected 2s infinite;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.status-reconnecting {
|
|
47
|
+
background: linear-gradient(135deg, #f6d365 0%, #fda085 100%);
|
|
48
|
+
color: #744210;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.status-reconnecting span {
|
|
52
|
+
animation: spin 1s linear infinite;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.status-disconnected {
|
|
56
|
+
background: linear-gradient(135deg, #e3e3e3 0%, #b8b8b8 100%);
|
|
57
|
+
color: #4a5568;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.status-stale {
|
|
61
|
+
background: linear-gradient(135deg, #ffeaa7 0%, #fdcb6e 100%);
|
|
62
|
+
color: #744210;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.status-stale::after {
|
|
66
|
+
content: '';
|
|
67
|
+
position: absolute;
|
|
68
|
+
top: 0;
|
|
69
|
+
left: -100%;
|
|
70
|
+
width: 100%;
|
|
71
|
+
height: 100%;
|
|
72
|
+
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
|
|
73
|
+
animation: shimmer 2s infinite;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.status-failed {
|
|
77
|
+
background: linear-gradient(135deg, #fc5c7d 0%, #fc5c7d 100%);
|
|
78
|
+
color: white;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Connection Quality Indicator */
|
|
82
|
+
.connection-quality {
|
|
83
|
+
display: flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
gap: 8px;
|
|
86
|
+
padding: 4px 8px;
|
|
87
|
+
background: #f7fafc;
|
|
88
|
+
border-radius: 8px;
|
|
89
|
+
font-size: 12px;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.quality-bar {
|
|
93
|
+
width: 60px;
|
|
94
|
+
height: 6px;
|
|
95
|
+
background: #e2e8f0;
|
|
96
|
+
border-radius: 3px;
|
|
97
|
+
overflow: hidden;
|
|
98
|
+
position: relative;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.quality-fill {
|
|
102
|
+
height: 100%;
|
|
103
|
+
transition: width 0.3s ease, background 0.3s ease;
|
|
104
|
+
border-radius: 3px;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.quality-good .quality-fill {
|
|
108
|
+
background: linear-gradient(90deg, #68d391, #48bb78);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.quality-moderate .quality-fill {
|
|
112
|
+
background: linear-gradient(90deg, #f6d365, #fda085);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.quality-poor .quality-fill {
|
|
116
|
+
background: linear-gradient(90deg, #fc5c7d, #fc5c7d);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.quality-text {
|
|
120
|
+
color: #4a5568;
|
|
121
|
+
font-weight: 500;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Latency Display */
|
|
125
|
+
.connection-latency {
|
|
126
|
+
display: inline-flex;
|
|
127
|
+
align-items: center;
|
|
128
|
+
padding: 2px 6px;
|
|
129
|
+
border-radius: 4px;
|
|
130
|
+
font-size: 11px;
|
|
131
|
+
font-weight: 600;
|
|
132
|
+
font-family: 'SF Mono', Monaco, monospace;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.latency-good {
|
|
136
|
+
background: #c6f6d5;
|
|
137
|
+
color: #22543d;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.latency-moderate {
|
|
141
|
+
background: #fed7aa;
|
|
142
|
+
color: #7c2d12;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.latency-poor {
|
|
146
|
+
background: #fed7d7;
|
|
147
|
+
color: #742a2a;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* Connection Metrics Panel */
|
|
151
|
+
.connection-metrics {
|
|
152
|
+
background: white;
|
|
153
|
+
border-radius: 12px;
|
|
154
|
+
padding: 16px;
|
|
155
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
|
156
|
+
margin-top: 16px;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.metrics-grid {
|
|
160
|
+
display: grid;
|
|
161
|
+
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
|
162
|
+
gap: 12px;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.metric-item {
|
|
166
|
+
text-align: center;
|
|
167
|
+
padding: 8px;
|
|
168
|
+
background: #f7fafc;
|
|
169
|
+
border-radius: 8px;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.metric-value {
|
|
173
|
+
font-size: 20px;
|
|
174
|
+
font-weight: 700;
|
|
175
|
+
color: #2d3748;
|
|
176
|
+
display: block;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.metric-label {
|
|
180
|
+
font-size: 11px;
|
|
181
|
+
color: #718096;
|
|
182
|
+
text-transform: uppercase;
|
|
183
|
+
letter-spacing: 0.5px;
|
|
184
|
+
margin-top: 4px;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* Notification Area */
|
|
188
|
+
.connection-notifications {
|
|
189
|
+
position: fixed;
|
|
190
|
+
top: 20px;
|
|
191
|
+
right: 20px;
|
|
192
|
+
z-index: 1000;
|
|
193
|
+
max-width: 320px;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.notification {
|
|
197
|
+
background: white;
|
|
198
|
+
padding: 12px 16px;
|
|
199
|
+
border-radius: 8px;
|
|
200
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
201
|
+
margin-bottom: 8px;
|
|
202
|
+
font-size: 14px;
|
|
203
|
+
opacity: 1;
|
|
204
|
+
transition: opacity 0.3s ease, transform 0.3s ease;
|
|
205
|
+
animation: slideIn 0.3s ease;
|
|
206
|
+
border-left: 4px solid;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.notification-info {
|
|
210
|
+
border-left-color: #4299e1;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.notification-success {
|
|
214
|
+
border-left-color: #48bb78;
|
|
215
|
+
background: #f0fff4;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.notification-warning {
|
|
219
|
+
border-left-color: #ed8936;
|
|
220
|
+
background: #fffdf7;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.notification-error {
|
|
224
|
+
border-left-color: #f56565;
|
|
225
|
+
background: #fff5f5;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* Connection Health Badge */
|
|
229
|
+
.connection-health {
|
|
230
|
+
display: inline-flex;
|
|
231
|
+
align-items: center;
|
|
232
|
+
gap: 6px;
|
|
233
|
+
padding: 4px 8px;
|
|
234
|
+
background: #f7fafc;
|
|
235
|
+
border-radius: 6px;
|
|
236
|
+
font-size: 12px;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.health-indicator {
|
|
240
|
+
width: 8px;
|
|
241
|
+
height: 8px;
|
|
242
|
+
border-radius: 50%;
|
|
243
|
+
animation: pulse 2s infinite;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.health-excellent {
|
|
247
|
+
background: #48bb78;
|
|
248
|
+
box-shadow: 0 0 0 2px rgba(72, 187, 120, 0.3);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.health-good {
|
|
252
|
+
background: #38b2ac;
|
|
253
|
+
box-shadow: 0 0 0 2px rgba(56, 178, 172, 0.3);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.health-fair {
|
|
257
|
+
background: #ed8936;
|
|
258
|
+
box-shadow: 0 0 0 2px rgba(237, 137, 54, 0.3);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.health-poor {
|
|
262
|
+
background: #f56565;
|
|
263
|
+
box-shadow: 0 0 0 2px rgba(245, 101, 101, 0.3);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/* Animations */
|
|
267
|
+
@keyframes pulse {
|
|
268
|
+
0%, 100% {
|
|
269
|
+
opacity: 1;
|
|
270
|
+
}
|
|
271
|
+
50% {
|
|
272
|
+
opacity: 0.5;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
@keyframes pulse-connected {
|
|
277
|
+
0%, 100% {
|
|
278
|
+
transform: scale(1);
|
|
279
|
+
opacity: 1;
|
|
280
|
+
}
|
|
281
|
+
50% {
|
|
282
|
+
transform: scale(1.2);
|
|
283
|
+
opacity: 0.8;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
@keyframes spin {
|
|
288
|
+
from {
|
|
289
|
+
transform: rotate(0deg);
|
|
290
|
+
}
|
|
291
|
+
to {
|
|
292
|
+
transform: rotate(360deg);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
@keyframes shimmer {
|
|
297
|
+
to {
|
|
298
|
+
left: 100%;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
@keyframes slideIn {
|
|
303
|
+
from {
|
|
304
|
+
transform: translateX(100%);
|
|
305
|
+
opacity: 0;
|
|
306
|
+
}
|
|
307
|
+
to {
|
|
308
|
+
transform: translateX(0);
|
|
309
|
+
opacity: 1;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/* Responsive Design */
|
|
314
|
+
@media (max-width: 768px) {
|
|
315
|
+
.connection-notifications {
|
|
316
|
+
right: 10px;
|
|
317
|
+
left: 10px;
|
|
318
|
+
max-width: none;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.connection-metrics {
|
|
322
|
+
padding: 12px;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.metrics-grid {
|
|
326
|
+
grid-template-columns: repeat(2, 1fr);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/* Dark Mode Support */
|
|
331
|
+
@media (prefers-color-scheme: dark) {
|
|
332
|
+
.connection-quality {
|
|
333
|
+
background: #2d3748;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.quality-bar {
|
|
337
|
+
background: #4a5568;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.quality-text {
|
|
341
|
+
color: #cbd5e0;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.connection-metrics {
|
|
345
|
+
background: #2d3748;
|
|
346
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.metric-item {
|
|
350
|
+
background: #1a202c;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.metric-value {
|
|
354
|
+
color: #e2e8f0;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.metric-label {
|
|
358
|
+
color: #a0aec0;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.notification {
|
|
362
|
+
background: #2d3748;
|
|
363
|
+
color: #e2e8f0;
|
|
364
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.connection-health {
|
|
368
|
+
background: #2d3748;
|
|
369
|
+
}
|
|
370
|
+
}
|