claude-mpm 4.2.1__py3-none-any.whl → 4.2.3__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/agentic_coder_optimizer.json +1 -1
- claude_mpm/agents/templates/api_qa.json +1 -1
- claude_mpm/agents/templates/code_analyzer.json +1 -1
- claude_mpm/agents/templates/data_engineer.json +1 -1
- claude_mpm/agents/templates/documentation.json +1 -1
- claude_mpm/agents/templates/engineer.json +2 -2
- claude_mpm/agents/templates/gcp_ops_agent.json +14 -9
- claude_mpm/agents/templates/imagemagick.json +1 -1
- claude_mpm/agents/templates/memory_manager.json +1 -1
- claude_mpm/agents/templates/ops.json +1 -1
- claude_mpm/agents/templates/project_organizer.json +1 -1
- claude_mpm/agents/templates/qa.json +2 -2
- claude_mpm/agents/templates/refactoring_engineer.json +1 -1
- claude_mpm/agents/templates/research.json +3 -3
- claude_mpm/agents/templates/security.json +1 -1
- claude_mpm/agents/templates/test-non-mpm.json +20 -0
- claude_mpm/agents/templates/ticketing.json +1 -1
- claude_mpm/agents/templates/vercel_ops_agent.json +2 -2
- claude_mpm/agents/templates/version_control.json +1 -1
- claude_mpm/agents/templates/web_qa.json +3 -8
- claude_mpm/agents/templates/web_ui.json +1 -1
- claude_mpm/cli/commands/agents.py +3 -0
- claude_mpm/cli/commands/dashboard.py +3 -3
- claude_mpm/cli/commands/monitor.py +227 -64
- claude_mpm/core/config.py +25 -0
- claude_mpm/core/unified_agent_registry.py +2 -2
- claude_mpm/dashboard/static/css/code-tree.css +220 -1
- claude_mpm/dashboard/static/css/dashboard.css +286 -0
- claude_mpm/dashboard/static/dist/components/code-tree.js +1 -1
- claude_mpm/dashboard/static/js/components/code-simple.js +507 -15
- claude_mpm/dashboard/static/js/components/code-tree.js +2044 -124
- claude_mpm/dashboard/static/js/socket-client.js +5 -2
- claude_mpm/dashboard/templates/code_simple.html +79 -0
- claude_mpm/dashboard/templates/index.html +42 -41
- claude_mpm/services/agents/deployment/agent_deployment.py +4 -1
- claude_mpm/services/agents/deployment/agent_discovery_service.py +101 -2
- claude_mpm/services/agents/deployment/agent_format_converter.py +53 -9
- claude_mpm/services/agents/deployment/agent_template_builder.py +355 -25
- claude_mpm/services/agents/deployment/agent_validator.py +11 -6
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +83 -15
- claude_mpm/services/agents/deployment/validation/template_validator.py +51 -40
- claude_mpm/services/cli/agent_listing_service.py +2 -2
- claude_mpm/services/dashboard/stable_server.py +389 -0
- claude_mpm/services/socketio/client_proxy.py +16 -0
- claude_mpm/services/socketio/dashboard_server.py +360 -0
- claude_mpm/services/socketio/handlers/code_analysis.py +27 -5
- claude_mpm/services/socketio/monitor_client.py +366 -0
- claude_mpm/services/socketio/monitor_server.py +505 -0
- claude_mpm/tools/code_tree_analyzer.py +95 -17
- {claude_mpm-4.2.1.dist-info → claude_mpm-4.2.3.dist-info}/METADATA +1 -1
- {claude_mpm-4.2.1.dist-info → claude_mpm-4.2.3.dist-info}/RECORD +57 -52
- {claude_mpm-4.2.1.dist-info → claude_mpm-4.2.3.dist-info}/WHEEL +0 -0
- {claude_mpm-4.2.1.dist-info → claude_mpm-4.2.3.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.2.1.dist-info → claude_mpm-4.2.3.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.2.1.dist-info → claude_mpm-4.2.3.dist-info}/top_level.txt +0 -0
|
@@ -1,31 +1,36 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Monitor command implementation for claude-mpm.
|
|
3
3
|
|
|
4
|
-
WHY: This module provides CLI commands for managing the
|
|
5
|
-
allowing users to start, stop, restart, and check status of the monitoring
|
|
6
|
-
The monitor
|
|
4
|
+
WHY: This module provides CLI commands for managing the lightweight monitoring server,
|
|
5
|
+
allowing users to start, stop, restart, and check status of the independent monitoring service.
|
|
6
|
+
The monitor service runs as a stable background service on port 8766 (configurable).
|
|
7
7
|
|
|
8
8
|
DESIGN DECISIONS:
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
9
|
+
- Use independent MonitorServer for decoupled architecture
|
|
10
|
+
- Monitor runs on port 8766, Dashboard runs on port 8765
|
|
11
|
+
- Support background mode by default for stable always-on operation
|
|
12
|
+
- Provide status checking and port configuration
|
|
13
|
+
- Maintain minimal dependencies and resource usage
|
|
13
14
|
"""
|
|
14
15
|
|
|
16
|
+
import signal
|
|
17
|
+
import sys
|
|
18
|
+
import time
|
|
15
19
|
from typing import Optional
|
|
16
20
|
|
|
17
21
|
from ...constants import MonitorCommands
|
|
22
|
+
from ...services.port_manager import PortManager
|
|
23
|
+
from ...services.socketio.monitor_server import MonitorServer
|
|
18
24
|
from ..shared import BaseCommand, CommandResult
|
|
19
|
-
from .dashboard import DashboardCommand
|
|
20
25
|
|
|
21
26
|
|
|
22
27
|
class MonitorCommand(BaseCommand):
|
|
23
|
-
"""Monitor command
|
|
28
|
+
"""Monitor command for managing the independent monitoring server."""
|
|
24
29
|
|
|
25
30
|
def __init__(self):
|
|
26
31
|
super().__init__("monitor")
|
|
27
|
-
|
|
28
|
-
self.
|
|
32
|
+
self.port_manager = PortManager()
|
|
33
|
+
self.server = None
|
|
29
34
|
|
|
30
35
|
def validate_args(self, args) -> Optional[str]:
|
|
31
36
|
"""Validate command arguments."""
|
|
@@ -38,25 +43,25 @@ class MonitorCommand(BaseCommand):
|
|
|
38
43
|
return None
|
|
39
44
|
|
|
40
45
|
def run(self, args) -> CommandResult:
|
|
41
|
-
"""Execute the monitor command
|
|
46
|
+
"""Execute the monitor command using independent MonitorServer."""
|
|
42
47
|
try:
|
|
43
|
-
self.logger.info("Monitor command
|
|
48
|
+
self.logger.info("Monitor command using independent monitoring server")
|
|
44
49
|
|
|
45
50
|
# Handle default case (no subcommand) - default to status
|
|
46
51
|
if not hasattr(args, "monitor_command") or not args.monitor_command:
|
|
47
|
-
return self.
|
|
52
|
+
return self._status_monitor(args)
|
|
48
53
|
|
|
49
|
-
#
|
|
54
|
+
# Route to specific monitor commands
|
|
50
55
|
if args.monitor_command == MonitorCommands.START.value:
|
|
51
|
-
return self.
|
|
56
|
+
return self._start_monitor(args)
|
|
52
57
|
if args.monitor_command == MonitorCommands.STOP.value:
|
|
53
|
-
return self.
|
|
58
|
+
return self._stop_monitor(args)
|
|
54
59
|
if args.monitor_command == MonitorCommands.RESTART.value:
|
|
55
|
-
return self.
|
|
60
|
+
return self._restart_monitor(args)
|
|
56
61
|
if args.monitor_command == MonitorCommands.STATUS.value:
|
|
57
|
-
return self.
|
|
62
|
+
return self._status_monitor(args)
|
|
58
63
|
if args.monitor_command == MonitorCommands.PORT.value:
|
|
59
|
-
return self.
|
|
64
|
+
return self._start_monitor_on_port(args)
|
|
60
65
|
|
|
61
66
|
return CommandResult.error_result(
|
|
62
67
|
f"Unknown monitor command: {args.monitor_command}"
|
|
@@ -66,66 +71,230 @@ class MonitorCommand(BaseCommand):
|
|
|
66
71
|
self.logger.error(f"Error executing monitor command: {e}", exc_info=True)
|
|
67
72
|
return CommandResult.error_result(f"Error executing monitor command: {e}")
|
|
68
73
|
|
|
69
|
-
def
|
|
70
|
-
"""Start the
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
args
|
|
75
|
-
#
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
def _start_monitor(self, args) -> CommandResult:
|
|
75
|
+
"""Start the monitor server."""
|
|
76
|
+
port = getattr(args, "port", 8765) # Default to 8765 for monitor
|
|
77
|
+
host = getattr(args, "host", "localhost")
|
|
78
|
+
background = getattr(
|
|
79
|
+
args, "background", True
|
|
80
|
+
) # Default to background for monitor
|
|
81
|
+
|
|
82
|
+
self.logger.info(
|
|
83
|
+
f"Starting monitor server on {host}:{port} (background: {background})"
|
|
84
|
+
)
|
|
78
85
|
|
|
79
|
-
|
|
86
|
+
# Check if monitor is already running
|
|
87
|
+
if self._is_monitor_running(port):
|
|
88
|
+
return CommandResult.success_result(
|
|
89
|
+
f"Monitor server already running on {host}:{port}",
|
|
90
|
+
data={"url": f"http://{host}:{port}", "port": port},
|
|
91
|
+
)
|
|
80
92
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
93
|
+
if background:
|
|
94
|
+
# Start monitor server in background
|
|
95
|
+
try:
|
|
96
|
+
self.server = MonitorServer(host=host, port=port)
|
|
97
|
+
if self.server.start_sync():
|
|
98
|
+
return CommandResult.success_result(
|
|
99
|
+
f"Monitor server started on {host}:{port}",
|
|
100
|
+
data={"url": f"http://{host}:{port}", "port": port},
|
|
101
|
+
)
|
|
102
|
+
return CommandResult.error_result("Failed to start monitor server")
|
|
103
|
+
except Exception as e:
|
|
104
|
+
return CommandResult.error_result(
|
|
105
|
+
f"Failed to start monitor server: {e}"
|
|
106
|
+
)
|
|
107
|
+
else:
|
|
108
|
+
# Run monitor in foreground mode
|
|
109
|
+
try:
|
|
110
|
+
print(f"Starting monitor server on {host}:{port}...")
|
|
111
|
+
print("Press Ctrl+C to stop the server")
|
|
112
|
+
|
|
113
|
+
self.server = MonitorServer(host=host, port=port)
|
|
114
|
+
|
|
115
|
+
# Set up signal handlers for graceful shutdown
|
|
116
|
+
def signal_handler(signum, frame):
|
|
117
|
+
print("\nShutting down monitor server...")
|
|
118
|
+
if self.server:
|
|
119
|
+
self.server.stop_sync()
|
|
120
|
+
sys.exit(0)
|
|
121
|
+
|
|
122
|
+
signal.signal(signal.SIGINT, signal_handler)
|
|
123
|
+
signal.signal(signal.SIGTERM, signal_handler)
|
|
124
|
+
|
|
125
|
+
# Start the server
|
|
126
|
+
if not self.server.start_sync():
|
|
127
|
+
return CommandResult.error_result("Failed to start monitor server")
|
|
128
|
+
|
|
129
|
+
# Keep the main thread alive while server is running
|
|
130
|
+
try:
|
|
131
|
+
while self.server.is_running():
|
|
132
|
+
time.sleep(1)
|
|
133
|
+
except KeyboardInterrupt:
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
# Stop the server
|
|
137
|
+
if self.server:
|
|
138
|
+
self.server.stop_sync()
|
|
139
|
+
|
|
140
|
+
return CommandResult.success_result("Monitor server stopped")
|
|
141
|
+
|
|
142
|
+
except KeyboardInterrupt:
|
|
143
|
+
print("\nMonitor server stopped by user")
|
|
144
|
+
return CommandResult.success_result("Monitor server stopped")
|
|
145
|
+
except Exception as e:
|
|
146
|
+
return CommandResult.error_result(
|
|
147
|
+
f"Failed to start monitor server: {e}"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def _stop_monitor(self, args) -> CommandResult:
|
|
151
|
+
"""Stop the monitor server."""
|
|
152
|
+
port = getattr(args, "port", 8766)
|
|
153
|
+
|
|
154
|
+
self.logger.info(f"Stopping monitor server on port {port}")
|
|
155
|
+
|
|
156
|
+
if not self._is_monitor_running(port):
|
|
157
|
+
return CommandResult.success_result(
|
|
158
|
+
f"No monitor server running on port {port}"
|
|
159
|
+
)
|
|
87
160
|
|
|
88
|
-
|
|
161
|
+
# Try to stop our server instance if we have one
|
|
162
|
+
if self.server and self.server.is_running():
|
|
163
|
+
try:
|
|
164
|
+
self.server.stop_sync()
|
|
165
|
+
return CommandResult.success_result(
|
|
166
|
+
f"Monitor server stopped on port {port}"
|
|
167
|
+
)
|
|
168
|
+
except Exception as e:
|
|
169
|
+
return CommandResult.error_result(f"Error stopping monitor server: {e}")
|
|
170
|
+
|
|
171
|
+
# If we don't have a server instance, try port manager cleanup
|
|
172
|
+
try:
|
|
173
|
+
self.port_manager.cleanup_dead_instances()
|
|
174
|
+
active_instances = self.port_manager.list_active_instances()
|
|
175
|
+
|
|
176
|
+
# Look for instances on the target port
|
|
177
|
+
for instance in active_instances:
|
|
178
|
+
if (
|
|
179
|
+
instance.get("port") == port
|
|
180
|
+
and instance.get("service_type") == "monitor"
|
|
181
|
+
):
|
|
182
|
+
# Found an instance, but we can't stop it directly
|
|
183
|
+
# This would need to be implemented with a proper process manager
|
|
184
|
+
return CommandResult.error_result(
|
|
185
|
+
f"Monitor server found on port {port} but cannot be stopped "
|
|
186
|
+
"(no direct control - you may need to kill the process manually)"
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
return CommandResult.success_result(
|
|
190
|
+
f"No monitor server found on port {port}"
|
|
191
|
+
)
|
|
89
192
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
193
|
+
except Exception as e:
|
|
194
|
+
return CommandResult.error_result(
|
|
195
|
+
f"Error checking monitor server status: {e}"
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
def _restart_monitor(self, args) -> CommandResult:
|
|
199
|
+
"""Restart the monitor server."""
|
|
200
|
+
self.logger.info("Restarting monitor server")
|
|
93
201
|
|
|
94
202
|
# Stop first
|
|
95
|
-
stop_result = self.
|
|
203
|
+
stop_result = self._stop_monitor(args)
|
|
96
204
|
if not stop_result.success:
|
|
97
|
-
self.logger.warning(
|
|
205
|
+
self.logger.warning(
|
|
206
|
+
"Failed to stop monitor server for restart, proceeding anyway"
|
|
207
|
+
)
|
|
98
208
|
|
|
99
209
|
# Wait a moment
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
time.sleep(1)
|
|
210
|
+
time.sleep(2)
|
|
103
211
|
|
|
104
212
|
# Start again
|
|
105
|
-
return self.
|
|
213
|
+
return self._start_monitor(args)
|
|
214
|
+
|
|
215
|
+
def _status_monitor(self, args) -> CommandResult:
|
|
216
|
+
"""Get monitor server status."""
|
|
217
|
+
port = getattr(args, "port", 8766)
|
|
218
|
+
verbose = getattr(args, "verbose", False)
|
|
219
|
+
show_ports = getattr(args, "show_ports", False)
|
|
220
|
+
|
|
221
|
+
# Check if monitor is running
|
|
222
|
+
monitor_running = self._is_monitor_running(port)
|
|
223
|
+
|
|
224
|
+
status_data = {
|
|
225
|
+
"running": monitor_running,
|
|
226
|
+
"default_port": port,
|
|
227
|
+
"service_type": "monitor",
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if monitor_running:
|
|
231
|
+
status_data["url"] = f"http://localhost:{port}"
|
|
232
|
+
|
|
233
|
+
# Check all ports if requested
|
|
234
|
+
if show_ports:
|
|
235
|
+
port_status = {}
|
|
236
|
+
for check_port in range(8766, 8776): # Monitor port range
|
|
237
|
+
is_running = self._is_monitor_running(check_port)
|
|
238
|
+
port_status[check_port] = {
|
|
239
|
+
"running": is_running,
|
|
240
|
+
"url": f"http://localhost:{check_port}" if is_running else None,
|
|
241
|
+
}
|
|
242
|
+
status_data["ports"] = port_status
|
|
243
|
+
|
|
244
|
+
# Get active instances from port manager
|
|
245
|
+
self.port_manager.cleanup_dead_instances()
|
|
246
|
+
active_instances = self.port_manager.list_active_instances()
|
|
247
|
+
if active_instances:
|
|
248
|
+
monitor_instances = [
|
|
249
|
+
inst
|
|
250
|
+
for inst in active_instances
|
|
251
|
+
if inst.get("service_type") == "monitor"
|
|
252
|
+
]
|
|
253
|
+
if monitor_instances:
|
|
254
|
+
status_data["active_instances"] = monitor_instances
|
|
255
|
+
|
|
256
|
+
if verbose and self.server:
|
|
257
|
+
status_data["server_stats"] = self.server.get_stats()
|
|
258
|
+
|
|
259
|
+
# Format output message
|
|
260
|
+
if monitor_running:
|
|
261
|
+
message = f"Monitor server is running at {status_data['url']}"
|
|
262
|
+
else:
|
|
263
|
+
message = "Monitor server is not running"
|
|
264
|
+
|
|
265
|
+
return CommandResult.success_result(message, data=status_data)
|
|
266
|
+
|
|
267
|
+
def _start_monitor_on_port(self, args) -> CommandResult:
|
|
268
|
+
"""Start monitor server on specific port."""
|
|
269
|
+
port = getattr(args, "port", 8766)
|
|
270
|
+
self.logger.info(f"Starting monitor server on port {port}")
|
|
106
271
|
|
|
107
|
-
def _status_dashboard(self, args) -> CommandResult:
|
|
108
|
-
"""Get dashboard service status."""
|
|
109
|
-
return self.dashboard_command._status_dashboard(args)
|
|
110
|
-
|
|
111
|
-
def _start_dashboard_on_port(self, args) -> CommandResult:
|
|
112
|
-
"""Start dashboard service on specific port."""
|
|
113
|
-
self.logger.info(
|
|
114
|
-
f"Starting dashboard service on port {getattr(args, 'port', 8765)}"
|
|
115
|
-
)
|
|
116
272
|
# Ensure background mode for port-specific starts
|
|
117
273
|
if not hasattr(args, "background"):
|
|
118
274
|
args.background = True
|
|
119
275
|
|
|
120
|
-
return self.
|
|
276
|
+
return self._start_monitor(args)
|
|
277
|
+
|
|
278
|
+
def _is_monitor_running(self, port: int) -> bool:
|
|
279
|
+
"""Check if monitor server is running on given port."""
|
|
280
|
+
import socket
|
|
281
|
+
|
|
282
|
+
try:
|
|
283
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
284
|
+
s.settimeout(1)
|
|
285
|
+
result = s.connect_ex(("localhost", port))
|
|
286
|
+
return result == 0
|
|
287
|
+
except Exception:
|
|
288
|
+
return False
|
|
121
289
|
|
|
122
290
|
|
|
123
291
|
def manage_monitor(args):
|
|
124
292
|
"""
|
|
125
293
|
Main entry point for monitor command.
|
|
126
294
|
|
|
127
|
-
The monitor command
|
|
128
|
-
|
|
295
|
+
The monitor command manages an independent lightweight monitoring server on port 8766.
|
|
296
|
+
This server runs separately from the dashboard (port 8765) and provides stable
|
|
297
|
+
event collection and relay services.
|
|
129
298
|
"""
|
|
130
299
|
command = MonitorCommand()
|
|
131
300
|
error = command.validate_args(args)
|
|
@@ -148,9 +317,3 @@ def manage_monitor(args):
|
|
|
148
317
|
if result.message:
|
|
149
318
|
print(f"Error: {result.message}")
|
|
150
319
|
return 1
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
# All legacy functions have been removed.
|
|
154
|
-
# The monitor command now delegates to the unified dashboard service.
|
|
155
|
-
# This consolidation provides a single service that handles both HTTP (port 8765)
|
|
156
|
-
# and Socket.IO (also on port 8765) rather than separate services on different ports.
|
claude_mpm/core/config.py
CHANGED
|
@@ -527,11 +527,36 @@ class Config:
|
|
|
527
527
|
},
|
|
528
528
|
},
|
|
529
529
|
},
|
|
530
|
+
# Monitor server configuration (decoupled from dashboard)
|
|
531
|
+
"monitor_server": {
|
|
532
|
+
"host": "localhost",
|
|
533
|
+
"port": 8765, # Default monitor port (shared with dashboard)
|
|
534
|
+
"enable_health_monitoring": True,
|
|
535
|
+
"auto_start": False, # Don't auto-start with dashboard by default
|
|
536
|
+
"event_buffer_size": 2000, # Larger buffer for monitor server
|
|
537
|
+
"client_timeout": 60, # Timeout for inactive clients
|
|
538
|
+
},
|
|
539
|
+
# Dashboard server configuration (connects to monitor)
|
|
540
|
+
"dashboard_server": {
|
|
541
|
+
"host": "localhost",
|
|
542
|
+
"port": 8765, # Dashboard UI port
|
|
543
|
+
"monitor_host": "localhost", # Monitor server host to connect to
|
|
544
|
+
"monitor_port": 8765, # Monitor server port to connect to
|
|
545
|
+
"auto_connect_monitor": True, # Automatically connect to monitor
|
|
546
|
+
"monitor_reconnect": True, # Auto-reconnect to monitor if disconnected
|
|
547
|
+
"fallback_standalone": True, # Run in standalone mode if monitor unavailable
|
|
548
|
+
},
|
|
530
549
|
# Agent deployment configuration
|
|
531
550
|
"agent_deployment": {
|
|
532
551
|
"excluded_agents": [], # List of agent IDs to exclude from deployment
|
|
533
552
|
"exclude_dependencies": False, # Whether to exclude agent dependencies too
|
|
534
553
|
"case_sensitive": False, # Whether agent name matching is case-sensitive
|
|
554
|
+
"filter_non_mpm_agents": True, # Filter out non-MPM agents by default
|
|
555
|
+
"mpm_author_patterns": [
|
|
556
|
+
"claude mpm",
|
|
557
|
+
"claude-mpm",
|
|
558
|
+
"anthropic",
|
|
559
|
+
], # Patterns for MPM agents
|
|
535
560
|
},
|
|
536
561
|
# Instruction reinforcement system configuration
|
|
537
562
|
"instruction_reinforcement": {
|
|
@@ -420,8 +420,8 @@ class UnifiedAgentRegistry:
|
|
|
420
420
|
for _i, line in enumerate(lines[1:], 1):
|
|
421
421
|
if line.strip() == "---":
|
|
422
422
|
break
|
|
423
|
-
if line.startswith("description:"):
|
|
424
|
-
return line.split(":", 1)[1].strip().strip("\"'")
|
|
423
|
+
if line.strip().startswith("description:"):
|
|
424
|
+
return line.strip().split(":", 1)[1].strip().strip("\"'")
|
|
425
425
|
|
|
426
426
|
# Look for first paragraph
|
|
427
427
|
for line in lines:
|
|
@@ -459,7 +459,7 @@
|
|
|
459
459
|
border-radius: 8px;
|
|
460
460
|
padding: 20px;
|
|
461
461
|
position: relative;
|
|
462
|
-
overflow:
|
|
462
|
+
overflow: hidden;
|
|
463
463
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
|
464
464
|
border-left: 1px solid #e2e8f0;
|
|
465
465
|
}
|
|
@@ -828,6 +828,8 @@
|
|
|
828
828
|
.code-node text {
|
|
829
829
|
font: 12px sans-serif;
|
|
830
830
|
pointer-events: none;
|
|
831
|
+
/* Smooth transitions for zoom-based text scaling */
|
|
832
|
+
transition: font-size 0.2s ease;
|
|
831
833
|
}
|
|
832
834
|
|
|
833
835
|
.code-node.module circle {
|
|
@@ -974,6 +976,8 @@
|
|
|
974
976
|
text-anchor: middle;
|
|
975
977
|
dominant-baseline: central;
|
|
976
978
|
font-weight: 500;
|
|
979
|
+
/* Smooth transitions for zoom-based scaling */
|
|
980
|
+
transition: font-size 0.2s ease;
|
|
977
981
|
}
|
|
978
982
|
|
|
979
983
|
.code-node.directory:hover .item-count-badge {
|
|
@@ -1405,4 +1409,219 @@
|
|
|
1405
1409
|
transform: translateX(0);
|
|
1406
1410
|
opacity: 1;
|
|
1407
1411
|
}
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
/* Back Button for Focused Directory View */
|
|
1415
|
+
.tree-control-btn.back-btn {
|
|
1416
|
+
background: #3182ce !important;
|
|
1417
|
+
color: white !important;
|
|
1418
|
+
border: 1px solid #2c5aa0 !important;
|
|
1419
|
+
font-weight: 600;
|
|
1420
|
+
margin-right: 10px;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
.tree-control-btn.back-btn:hover {
|
|
1424
|
+
background: #2c5aa0 !important;
|
|
1425
|
+
transform: translateY(-1px);
|
|
1426
|
+
box-shadow: 0 4px 8px rgba(49, 130, 206, 0.3);
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
/* Focused Directory View Styling */
|
|
1430
|
+
.code-tree-container.focused {
|
|
1431
|
+
border: 2px solid #3182ce;
|
|
1432
|
+
border-radius: 8px;
|
|
1433
|
+
background: linear-gradient(135deg, #f7fafc 0%, #edf2f7 100%);
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
.code-tree-container.focused .tree-controls-toolbar {
|
|
1437
|
+
background: #e6fffa;
|
|
1438
|
+
border-bottom: 1px solid #81e6d9;
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
/* Corner Controls Styling */
|
|
1442
|
+
.tree-corner-controls {
|
|
1443
|
+
position: absolute;
|
|
1444
|
+
z-index: 1000;
|
|
1445
|
+
background: rgba(255, 255, 255, 0.95);
|
|
1446
|
+
border: 1px solid #e2e8f0;
|
|
1447
|
+
border-radius: 6px;
|
|
1448
|
+
padding: 8px 12px;
|
|
1449
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
1450
|
+
backdrop-filter: blur(4px);
|
|
1451
|
+
font-size: 12px;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
.tree-corner-controls.top-left {
|
|
1455
|
+
top: 10px;
|
|
1456
|
+
left: 10px;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
.tree-corner-controls.top-right {
|
|
1460
|
+
top: 10px;
|
|
1461
|
+
right: 10px;
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
.tree-corner-controls.bottom-left {
|
|
1465
|
+
bottom: 10px;
|
|
1466
|
+
left: 10px;
|
|
1467
|
+
display: flex;
|
|
1468
|
+
flex-direction: column;
|
|
1469
|
+
align-items: flex-start;
|
|
1470
|
+
gap: 4px;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
.tree-corner-controls.bottom-right {
|
|
1474
|
+
bottom: 10px;
|
|
1475
|
+
right: 10px;
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
.tree-corner-controls .control-group {
|
|
1479
|
+
display: flex;
|
|
1480
|
+
align-items: center;
|
|
1481
|
+
gap: 8px;
|
|
1482
|
+
flex-wrap: wrap;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
.tree-corner-controls .control-label {
|
|
1486
|
+
font-weight: 600;
|
|
1487
|
+
color: #4a5568;
|
|
1488
|
+
white-space: nowrap;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
.tree-corner-controls .checkbox-group {
|
|
1492
|
+
display: flex;
|
|
1493
|
+
gap: 12px;
|
|
1494
|
+
flex-wrap: wrap;
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
.tree-corner-controls .checkbox-label {
|
|
1498
|
+
display: flex;
|
|
1499
|
+
align-items: center;
|
|
1500
|
+
gap: 4px;
|
|
1501
|
+
font-size: 11px;
|
|
1502
|
+
color: #4a5568;
|
|
1503
|
+
cursor: pointer;
|
|
1504
|
+
white-space: nowrap;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
.tree-corner-controls .checkbox-label input[type="checkbox"] {
|
|
1508
|
+
margin: 0;
|
|
1509
|
+
transform: scale(0.9);
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
.tree-corner-controls .select-compact,
|
|
1513
|
+
.tree-corner-controls .search-compact,
|
|
1514
|
+
.tree-corner-controls .input-compact {
|
|
1515
|
+
font-size: 11px;
|
|
1516
|
+
padding: 4px 8px;
|
|
1517
|
+
border: 1px solid #d1d5db;
|
|
1518
|
+
border-radius: 4px;
|
|
1519
|
+
background: white;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
.tree-corner-controls .search-compact {
|
|
1523
|
+
width: 120px;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
.tree-corner-controls .input-compact {
|
|
1527
|
+
width: 140px;
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
.tree-corner-controls .stats-display {
|
|
1531
|
+
font-size: 11px;
|
|
1532
|
+
color: #6b7280;
|
|
1533
|
+
font-weight: 500;
|
|
1534
|
+
margin-bottom: 4px;
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
.tree-corner-controls .status-display {
|
|
1538
|
+
font-size: 10px;
|
|
1539
|
+
color: #4b5563;
|
|
1540
|
+
font-style: italic;
|
|
1541
|
+
opacity: 0.8;
|
|
1542
|
+
max-width: 200px;
|
|
1543
|
+
overflow: hidden;
|
|
1544
|
+
text-overflow: ellipsis;
|
|
1545
|
+
white-space: nowrap;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
.tree-corner-controls .status-display .breadcrumb-ticker {
|
|
1549
|
+
display: block;
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
.tree-corner-controls .status-display #breadcrumb-content {
|
|
1553
|
+
display: inline-block;
|
|
1554
|
+
animation: none; /* Disable any existing animations for corner display */
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
/* Hide corner controls when in focused mode to avoid clutter */
|
|
1558
|
+
.code-tree-container.focused .tree-corner-controls {
|
|
1559
|
+
opacity: 0.7;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
.code-tree-container.focused .tree-corner-controls:hover {
|
|
1563
|
+
opacity: 1;
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
/* Force horizontal text for specific nodes */
|
|
1567
|
+
.node-label.horizontal-text {
|
|
1568
|
+
writing-mode: horizontal-tb !important;
|
|
1569
|
+
text-orientation: mixed !important;
|
|
1570
|
+
transform: rotate(0deg) !important;
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
/* Zoom controls styling */
|
|
1574
|
+
.zoom-level-display {
|
|
1575
|
+
display: inline-block;
|
|
1576
|
+
padding: 2px 6px;
|
|
1577
|
+
background: rgba(0, 0, 0, 0.05);
|
|
1578
|
+
border-radius: 3px;
|
|
1579
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
1580
|
+
font-size: 11px !important;
|
|
1581
|
+
color: #718096 !important;
|
|
1582
|
+
margin-left: 8px !important;
|
|
1583
|
+
vertical-align: middle;
|
|
1584
|
+
min-width: 35px;
|
|
1585
|
+
text-align: center;
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
/* Enhanced tree control buttons for zoom */
|
|
1589
|
+
.tree-control-btn {
|
|
1590
|
+
background: #f8f9fa;
|
|
1591
|
+
border: 1px solid #e2e8f0;
|
|
1592
|
+
border-radius: 4px;
|
|
1593
|
+
padding: 6px 8px;
|
|
1594
|
+
margin: 0 2px;
|
|
1595
|
+
cursor: pointer;
|
|
1596
|
+
font-size: 12px;
|
|
1597
|
+
color: #4a5568;
|
|
1598
|
+
transition: all 0.2s;
|
|
1599
|
+
display: inline-block;
|
|
1600
|
+
vertical-align: middle;
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
.tree-control-btn:hover {
|
|
1604
|
+
background: #e2e8f0;
|
|
1605
|
+
border-color: #cbd5e0;
|
|
1606
|
+
color: #2d3748;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
.tree-control-btn:active {
|
|
1610
|
+
background: #cbd5e0;
|
|
1611
|
+
transform: translateY(1px);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
/* Zoom-specific button styling */
|
|
1615
|
+
.tree-control-btn[title*="Zoom"] {
|
|
1616
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
1617
|
+
font-weight: 500;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
/* Pan cursor when dragging */
|
|
1621
|
+
.code-tree-container svg {
|
|
1622
|
+
cursor: grab;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
.code-tree-container svg:active {
|
|
1626
|
+
cursor: grabbing;
|
|
1408
1627
|
}
|