claude-mpm 4.1.10__py3-none-any.whl → 4.1.12__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/INSTRUCTIONS.md +8 -0
- claude_mpm/cli/__init__.py +11 -0
- claude_mpm/cli/commands/analyze.py +2 -1
- claude_mpm/cli/commands/configure.py +9 -8
- claude_mpm/cli/commands/configure_tui.py +3 -1
- claude_mpm/cli/commands/dashboard.py +288 -0
- claude_mpm/cli/commands/debug.py +0 -1
- claude_mpm/cli/commands/mpm_init.py +442 -0
- claude_mpm/cli/commands/mpm_init_handler.py +84 -0
- claude_mpm/cli/parsers/base_parser.py +15 -0
- claude_mpm/cli/parsers/dashboard_parser.py +113 -0
- claude_mpm/cli/parsers/mpm_init_parser.py +128 -0
- claude_mpm/constants.py +10 -0
- claude_mpm/core/config.py +18 -0
- claude_mpm/core/instruction_reinforcement_hook.py +266 -0
- claude_mpm/core/pm_hook_interceptor.py +105 -8
- claude_mpm/dashboard/analysis_runner.py +52 -25
- claude_mpm/dashboard/static/built/components/activity-tree.js +1 -1
- claude_mpm/dashboard/static/built/components/code-tree.js +2 -0
- claude_mpm/dashboard/static/built/components/code-viewer.js +2 -0
- claude_mpm/dashboard/static/built/components/event-viewer.js +1 -1
- claude_mpm/dashboard/static/built/dashboard.js +1 -1
- claude_mpm/dashboard/static/built/socket-client.js +1 -1
- claude_mpm/dashboard/static/css/code-tree.css +330 -1
- claude_mpm/dashboard/static/dist/components/activity-tree.js +1 -1
- claude_mpm/dashboard/static/dist/components/code-tree.js +2593 -2
- claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/dashboard.js +1 -1
- claude_mpm/dashboard/static/dist/socket-client.js +1 -1
- claude_mpm/dashboard/static/js/components/activity-tree.js +212 -13
- claude_mpm/dashboard/static/js/components/build-tracker.js +15 -13
- claude_mpm/dashboard/static/js/components/code-tree.js +2503 -917
- claude_mpm/dashboard/static/js/components/event-viewer.js +58 -19
- claude_mpm/dashboard/static/js/dashboard.js +46 -44
- claude_mpm/dashboard/static/js/socket-client.js +74 -32
- claude_mpm/dashboard/templates/index.html +25 -20
- claude_mpm/services/agents/deployment/agent_template_builder.py +11 -7
- claude_mpm/services/agents/memory/memory_format_service.py +3 -1
- claude_mpm/services/cli/agent_cleanup_service.py +1 -4
- claude_mpm/services/cli/socketio_manager.py +39 -8
- claude_mpm/services/cli/startup_checker.py +0 -1
- claude_mpm/services/core/cache_manager.py +0 -1
- claude_mpm/services/infrastructure/monitoring.py +1 -1
- claude_mpm/services/socketio/event_normalizer.py +64 -0
- claude_mpm/services/socketio/handlers/code_analysis.py +449 -0
- claude_mpm/services/socketio/server/connection_manager.py +3 -1
- claude_mpm/tools/code_tree_analyzer.py +930 -24
- claude_mpm/tools/code_tree_builder.py +0 -1
- claude_mpm/tools/code_tree_events.py +113 -15
- {claude_mpm-4.1.10.dist-info → claude_mpm-4.1.12.dist-info}/METADATA +2 -1
- {claude_mpm-4.1.10.dist-info → claude_mpm-4.1.12.dist-info}/RECORD +56 -48
- {claude_mpm-4.1.10.dist-info → claude_mpm-4.1.12.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.10.dist-info → claude_mpm-4.1.12.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.10.dist-info → claude_mpm-4.1.12.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.10.dist-info → claude_mpm-4.1.12.dist-info}/top_level.txt +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const t=window.io;class e{constructor(){this.socket=null,this.port=null,this.connectionCallbacks={connect:[],disconnect:[],error:[],event:[]},this.eventSchema={required:["source","type","subtype","timestamp","data"],optional:["event","session_id"]},this.isConnected=!1,this.isConnecting=!1,this.lastConnectTime=null,this.disconnectTime=null,this.events=[],this.sessions=new Map,this.currentSessionId=null,this.eventQueue=[],this.maxQueueSize=100,this.retryAttempts=0,this.maxRetryAttempts=5,this.retryDelays=[1e3,2e3,3e3,4e3,5e3],this.pendingEmissions=new Map,this.lastPingTime=null,this.lastPongTime=null,this.pingTimeout=9e4,this.healthCheckInterval=null,this.startStatusCheckFallback(),this.startHealthMonitoring()}connect(t="8765"){this.port=t;const e=`http://localhost:${t}`;if(this.socket&&(this.socket.connected||this.socket.connecting))return console.log("Already connected or connecting, disconnecting first..."),this.socket.disconnect(),void setTimeout(()=>this.doConnect(e),100);this.doConnect(e)}doConnect(e){if(console.log(`Connecting to Socket.IO server at ${e}`),void 0===t)return console.error("Socket.IO library not loaded! Make sure socket.io.min.js is loaded before this script."),void this.notifyConnectionStatus("Socket.IO library not loaded","error");this.isConnecting=!0,this.notifyConnectionStatus("Connecting...","connecting"),this.socket=t(e,{autoConnect:!0,reconnection:!0,reconnectionDelay:1e3,reconnectionDelayMax:5e3,reconnectionAttempts:5,timeout:2e4,forceNew:!0,transports:["websocket","polling"],pingInterval:45e3,pingTimeout:2e4}),this.setupSocketHandlers()}setupSocketHandlers(){this.socket.on("connect",()=>{console.log("Connected to Socket.IO server");const t=this.isConnected;if(this.isConnected=!0,this.isConnecting=!1,this.lastConnectTime=Date.now(),this.retryAttempts=0,this.disconnectTime&&!1===t){const t=(Date.now()-this.disconnectTime)/1e3;console.log(`Reconnected after ${t.toFixed(1)}s downtime`),this.flushEventQueue()}this.notifyConnectionStatus("Connected","connected"),window.socket=this.socket,console.log("SocketClient: Exposed socket globally as window.socket"),this.connectionCallbacks.connect.forEach(t=>t(this.socket.id)),this.requestStatus()}),this.socket.on("disconnect",t=>{const e={reason:t,timestamp:(new Date).toISOString(),wasConnected:this.isConnected,uptimeSeconds:this.lastConnectTime?((Date.now()-this.lastConnectTime)/1e3).toFixed(1):0,lastPing:this.lastPingTime?((Date.now()-this.lastPingTime)/1e3).toFixed(1)+"s ago":"never",lastPong:this.lastPongTime?((Date.now()-this.lastPongTime)/1e3).toFixed(1)+"s ago":"never"};console.log("Disconnected from server:",e),this.isConnected=!1,this.isConnecting=!1,this.disconnectTime=Date.now(),this.notifyConnectionStatus(`Disconnected: ${t}`,"disconnected"),this.connectionCallbacks.disconnect.forEach(e=>e(t));["transport close","ping timeout","transport error","io server disconnect"].includes(t)?(console.log(`Auto-reconnect triggered for reason: ${t}`),this.scheduleReconnect()):"io client disconnect"===t?console.log("Client-initiated disconnect, not auto-reconnecting"):(console.log(`Unknown disconnect reason: ${t}, attempting reconnect anyway`),this.scheduleReconnect())}),this.socket.on("connect_error",t=>{console.error("Connection error:",t),this.isConnecting=!1;const e=t.message||t.description||"Unknown error";this.notifyConnectionStatus(`Connection Error: ${e}`,"disconnected"),this.addEvent({type:"connection.error",timestamp:(new Date).toISOString(),data:{error:e,url:this.socket.io.uri,retry_attempt:this.retryAttempts}}),this.connectionCallbacks.error.forEach(t=>t(e)),this.scheduleReconnect()}),this.socket.on("claude_event",t=>{console.log("Received claude_event:",t);const e=this.validateEventSchema(t);if(!e)return void console.warn("Invalid event schema received:",t);if(e.type&&e.type.startsWith("code:"))return void console.log("Code analysis event received via claude_event, not adding to events list:",e.type);const n=this.transformEvent(e);console.log("Transformed event:",n),this.addEvent(n)}),this.socket.on("ping",t=>{this.lastPingTime=Date.now(),this.socket.emit("pong",{timestamp:t.timestamp,client_time:Date.now()})}),this.socket.on("pong",t=>{this.lastPongTime=Date.now()}),this.socket.on("session.started",t=>{this.addEvent({type:"session",subtype:"started",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("session.ended",t=>{this.addEvent({type:"session",subtype:"ended",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("claude.request",t=>{this.addEvent({type:"claude",subtype:"request",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("claude.response",t=>{this.addEvent({type:"claude",subtype:"response",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("agent.loaded",t=>{this.addEvent({type:"agent",subtype:"loaded",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("agent.executed",t=>{this.addEvent({type:"agent",subtype:"executed",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("hook.pre",t=>{this.addEvent({type:"hook",subtype:"pre",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("hook.post",t=>{this.addEvent({type:"hook",subtype:"post",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("todo.updated",t=>{this.addEvent({type:"todo",subtype:"updated",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("memory.operation",t=>{this.addEvent({type:"memory",subtype:"operation",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("log.entry",t=>{this.addEvent({type:"log",subtype:"entry",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("code:analysis:queued",t=>{console.log("Code analysis queued event received, not adding to events list")}),this.socket.on("code:analysis:accepted",t=>{console.log("Code analysis accepted event received, not adding to events list")}),this.socket.on("code:analysis:start",t=>{console.log("Code analysis start event received, not adding to events list")}),this.socket.on("code:analysis:complete",t=>{console.log("Code analysis complete event received, not adding to events list")}),this.socket.on("code:analysis:error",t=>{console.log("Code analysis error event received, not adding to events list")}),this.socket.on("code:file:start",t=>{console.log("Code file start event received, not adding to events list")}),this.socket.on("code:node:found",t=>{console.log("Code node found event received, not adding to events list")}),this.socket.on("code:analysis:progress",t=>{console.log("Code analysis progress event received, not adding to events list")}),this.socket.on("history",t=>{console.log("Received event history:",t),t&&Array.isArray(t.events)?(console.log(`Processing ${t.events.length} historical events (${t.count} sent, ${t.total_available} total available)`),t.events.forEach(t=>{const e=this.transformEvent(t);this.addEvent(e,!1)}),this.notifyEventUpdate(),console.log(`Event history loaded: ${t.events.length} events added to dashboard`)):Array.isArray(t)&&(console.log("Received legacy event history format:",t.length,"events"),t.forEach(t=>{const e=this.transformEvent(t);this.addEvent(e,!1)}),this.notifyEventUpdate())}),this.socket.on("system.status",t=>{console.log("Received system status:",t),t.sessions&&this.updateSessions(t.sessions),t.current_session&&(this.currentSessionId=t.current_session)})}disconnect(){this.socket&&(this.socket.disconnect(),this.socket=null),this.port=null,this.isConnected=!1,this.isConnecting=!1}emitWithRetry(t,e=null,n={}){const{maxRetries:s=3,retryDelays:o=[1e3,2e3,4e3],onSuccess:i=null,onFailure:c=null}=n,a=`${t}_${Date.now()}_${Math.random()}`,l=(n=0)=>{if(this.socket&&this.socket.connected)try{this.socket.emit(t,e),console.log(`Emitted ${t} successfully`),this.pendingEmissions.delete(a),i&&i()}catch(r){if(console.error(`Failed to emit ${t} (attempt ${n+1}):`,r),n<s-1){const s=o[n]||o[o.length-1];console.log(`Retrying ${t} in ${s}ms...`),this.pendingEmissions.set(a,{event:t,data:e,attemptNum:n+1,scheduledTime:Date.now()+s}),setTimeout(()=>l(n+1),s)}else console.error(`Failed to emit ${t} after ${s} attempts`),this.pendingEmissions.delete(a),c&&c("max_retries_exceeded")}else 0===n&&(this.queueEvent(t,e),console.log(`Queued ${t} for later emission (disconnected)`),c&&c("disconnected"))};l()}queueEvent(t,e){if(this.eventQueue.length>=this.maxQueueSize){const t=this.eventQueue.shift();console.warn(`Event queue full, dropped oldest event: ${t.event}`)}this.eventQueue.push({event:t,data:e,timestamp:Date.now()})}flushEventQueue(){if(0===this.eventQueue.length)return;console.log(`Flushing ${this.eventQueue.length} queued events...`);const t=[...this.eventQueue];this.eventQueue=[],t.forEach((t,e)=>{setTimeout(()=>{this.socket&&this.socket.connected&&(this.socket.emit(t.event,t.data),console.log(`Flushed queued event: ${t.event}`))},100*e)})}scheduleReconnect(){if(this.retryAttempts>=this.maxRetryAttempts)return console.log("Max reconnection attempts reached, stopping auto-reconnect"),void this.notifyConnectionStatus("Reconnection failed","disconnected");const t=this.retryDelays[this.retryAttempts]||this.retryDelays[this.retryDelays.length-1];this.retryAttempts++,console.log(`Scheduling reconnect attempt ${this.retryAttempts}/${this.maxRetryAttempts} in ${t}ms...`),this.notifyConnectionStatus(`Reconnecting in ${t/1e3}s...`,"connecting"),setTimeout(()=>{!this.isConnected&&this.port&&(console.log(`Attempting reconnection ${this.retryAttempts}/${this.maxRetryAttempts}...`),this.connect(this.port))},t)}requestStatus(){this.socket&&this.socket.connected&&(console.log("Requesting server status..."),this.emitWithRetry("request.status",null,{maxRetries:2,retryDelays:[500,1e3]}))}requestHistory(t={}){if(this.socket&&this.socket.connected){const e={limit:t.limit||50,event_types:t.event_types||[]};console.log("Requesting event history...",e),this.emitWithRetry("get_history",e,{maxRetries:3,retryDelays:[1e3,2e3,3e3],onFailure:t=>{console.error(`Failed to request history: ${t}`)}})}else console.warn("Cannot request history: not connected to server")}addEvent(t,e=!0){if(t.timestamp||(t.timestamp=(new Date).toISOString()),t.id||(t.id=Date.now()+Math.random()),this.events.push(t),t.data&&t.data.session_id){const e=t.data.session_id;this.sessions.has(e)||this.sessions.set(e,{id:e,startTime:t.timestamp,lastActivity:t.timestamp,eventCount:0,working_directory:null,git_branch:null});const n=this.sessions.get(e);n.lastActivity=t.timestamp,n.eventCount++;const s=[t.data.cwd,t.data.working_directory,t.data.working_dir,t.data.workingDirectory,t.data.instance_info?.working_dir,t.data.instance_info?.working_directory,t.data.instance_info?.cwd,t.cwd,t.working_directory,t.working_dir];for(const t of s)if(t&&"string"==typeof t&&t.trim()){n.working_directory=t,console.log(`[SOCKET-CLIENT] Found working directory for session ${e}:`,t);break}t.data.git_branch?n.git_branch=t.data.git_branch:t.data.instance_info&&t.data.instance_info.git_branch&&(n.git_branch=t.data.instance_info.git_branch)}e&&this.notifyEventUpdate()}updateSessions(t){Array.isArray(t)&&t.forEach(t=>{this.sessions.set(t.id,t)})}clearEvents(){this.events=[],this.sessions.clear(),this.notifyEventUpdate()}refreshHistory(t={}){this.clearEvents(),this.requestHistory(t)}getEventsBySession(t=null){return t?this.events.filter(e=>e.data&&e.data.session_id===t):this.events}onConnection(t,e){this.connectionCallbacks[t]&&this.connectionCallbacks[t].push(e)}onEventUpdate(t){this.connectionCallbacks.event.push(t)}on(t,e){if(this.socket)return this.socket.on(t,e);console.warn(`Cannot subscribe to '${t}': socket not initialized`)}off(t,e){if(this.socket)return this.socket.off(t,e);console.warn(`Cannot unsubscribe from '${t}': socket not initialized`)}notifyConnectionStatus(t,e){console.log(`SocketClient: Connection status changed to '${t}' (${e})`),this.updateConnectionStatusDOM(t,e),document.dispatchEvent(new CustomEvent("socketConnectionStatus",{detail:{status:t,type:e}}))}updateConnectionStatusDOM(t,e){const n=document.getElementById("connection-status");n?(n.innerHTML=`<span>●</span> ${t}`,n.className=`status-badge status-${e}`,console.log(`SocketClient: Direct DOM update - status: '${t}' (${e})`)):console.warn("SocketClient: Could not find connection-status element in DOM")}notifyEventUpdate(){this.connectionCallbacks.event.forEach(t=>t(this.events,this.sessions)),document.dispatchEvent(new CustomEvent("socketEventUpdate",{detail:{events:this.events,sessions:this.sessions}}))}getConnectionState(){return{isConnected:this.isConnected,isConnecting:this.isConnecting,socketId:this.socket?this.socket.id:null}}validateEventSchema(t){if(!t||"object"!=typeof t)return console.warn("Event data is not an object:",t),null;const e={...t};return e.source||(e.source="system"),e.type||(e.event?e.type=e.event:e.type="unknown"),e.subtype||(e.subtype="generic"),e.timestamp||(e.timestamp=(new Date).toISOString()),e.data||(e.data={}),e.data&&"object"!=typeof e.data&&(e.data={value:e.data}),console.log("Validated event:",e),e}transformEvent(t){if(!t)return t;let e={...t};if(!t.type&&t.event){const n=t.event;"TestStart"===n||"TestEnd"===n?(e.type="test",e.subtype=n.toLowerCase().replace("test","")):"SubagentStart"===n||"SubagentStop"===n?(e.type="subagent",e.subtype=n.toLowerCase().replace("subagent","")):"ToolCall"===n?(e.type="tool",e.subtype="call"):"UserPrompt"===n?(e.type="hook",e.subtype="user_prompt"):(e.type="unknown",e.subtype=n.toLowerCase(),e.type===e.subtype&&(e.subtype="event")),delete e.event}else if(t.type){const n=t.type;if(n.startsWith("hook.")){const t=n.substring(5);e.type="hook",e.subtype=t}else if(n.startsWith("code:"))e.type="code",e.subtype=n.substring(5);else if(n.includes(".")){const[t,...s]=n.split(".");e.type=t,e.subtype=s.join(".")}}else e.type="unknown",e.subtype="";if(!t.type&&t.event?e.originalEventName=t.event:t.type&&(e.originalEventName=t.type),t.data&&"object"==typeof t.data){const n=["type","subtype","timestamp","id","event","event_type","originalEventName"];Object.keys(t.data).forEach(s=>{n.includes(s)?console.warn(`Protected field '${s}' in data object was not copied to top level to preserve event structure`):"tool_parameters"===s&&"object"==typeof t.data[s]?e[s]=JSON.parse(JSON.stringify(t.data[s])):e[s]=t.data[s]}),e.data=t.data}if("hook"===e.type?"pre_tool"===e.subtype?e.hook_event_name="PreToolUse":"post_tool"===e.subtype?e.hook_event_name="PostToolUse":"subagent_start"===e.subtype?e.hook_event_name="SubagentStart":"subagent_stop"===e.subtype?e.hook_event_name="SubagentStop":"todo_write"===e.subtype?e.hook_event_name="TodoWrite":"start"===e.subtype?e.hook_event_name="Start":"stop"===e.subtype&&(e.hook_event_name="Stop"):"subagent"===e.type?"start"===e.subtype?e.hook_event_name="SubagentStart":"stop"===e.subtype&&(e.hook_event_name="SubagentStop"):"todo"===e.type&&"updated"===e.subtype&&(e.hook_event_name="TodoWrite"),"hook"===e.type&&("pre_tool"===e.subtype||"post_tool"===e.subtype)){console.log("Transformed tool event:",{type:e.type,subtype:e.subtype,hook_event_name:e.hook_event_name,tool_name:e.tool_name,has_tool_parameters:!!e.tool_parameters,tool_parameters:e.tool_parameters,has_data:!!e.data,keys:Object.keys(e).filter(t=>"data"!==t)});["Read","Write","Edit","MultiEdit","NotebookEdit"].includes(e.tool_name)&&console.log("File tool event details:",{tool_name:e.tool_name,file_path:e.tool_parameters?.file_path,path:e.tool_parameters?.path,notebook_path:e.tool_parameters?.notebook_path,full_parameters:e.tool_parameters})}return e}getState(){return{events:this.events,sessions:this.sessions,currentSessionId:this.currentSessionId}}startHealthMonitoring(){this.healthCheckInterval=setInterval(()=>{if(this.isConnected&&this.lastPingTime){const t=Date.now()-this.lastPingTime;t>this.pingTimeout&&(console.warn(`No ping from server for ${t/1e3}s, connection may be stale`),this.socket&&(console.log("Forcing reconnection due to stale connection..."),this.socket.disconnect(),setTimeout(()=>{this.port&&this.connect(this.port)},1e3)))}},1e4)}stopHealthMonitoring(){this.healthCheckInterval&&(clearInterval(this.healthCheckInterval),this.healthCheckInterval=null)}startStatusCheckFallback(){setInterval(()=>{this.checkAndUpdateStatus()},2e3),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{setTimeout(()=>this.checkAndUpdateStatus(),100)}):setTimeout(()=>this.checkAndUpdateStatus(),100)}checkAndUpdateStatus(){let t="Disconnected",e="disconnected";this.socket&&(this.socket.connected?(t="Connected",e="connected",this.isConnected=!0,this.isConnecting=!1):this.socket.connecting||this.isConnecting?(t="Connecting...",e="connecting",this.isConnected=!1):(t="Disconnected",e="disconnected",this.isConnected=!1,this.isConnecting=!1));const n=document.getElementById("connection-status");if(n){const s=n.textContent.replace("●","").trim(),o=n.className,i=`status-badge status-${e}`;s===t&&o===i||(console.log(`SocketClient: Fallback update - was '${s}' (${o}), now '${t}' (${i})`),this.updateConnectionStatusDOM(t,e))}}destroy(){this.stopHealthMonitoring(),this.socket&&(this.socket.disconnect(),this.socket=null),this.eventQueue=[],this.pendingEmissions.clear()}getConnectionMetrics(){return{isConnected:this.isConnected,uptime:this.lastConnectTime?(Date.now()-this.lastConnectTime)/1e3:0,lastPing:this.lastPingTime?(Date.now()-this.lastPingTime)/1e3:null,queuedEvents:this.eventQueue.length,pendingEmissions:this.pendingEmissions.size,retryAttempts:this.retryAttempts}}}window.SocketClient=e;class n{constructor(){this.socketClient=null,this.connectionCallbacks=new Set,this.eventUpdateCallbacks=new Set,this.socketClient=new e,window.socketClient=this.socketClient,this.setupSocketEventHandlers(),setTimeout(()=>{this.updateInitialConnectionStatus()},100),console.log("Socket manager initialized")}setupSocketEventHandlers(){document.addEventListener("socketConnectionStatus",t=>{console.log(`SocketManager: Processing connection status update: ${t.detail.status} (${t.detail.type})`),this.handleConnectionStatusChange(t.detail.status,t.detail.type),this.connectionCallbacks.forEach(e=>{try{e(t.detail.status,t.detail.type)}catch(n){console.error("Error in connection callback:",n)}})}),this.socketClient&&this.socketClient.onEventUpdate(t=>{this.eventUpdateCallbacks.forEach(e=>{try{e(t)}catch(n){console.error("Error in event update callback:",n)}})})}handleConnectionStatusChange(t,e){this.updateConnectionStatus(t,e),"connected"===e&&this.socketClient&&this.socketClient.socket&&(window.socket=this.socketClient.socket,console.log("SocketManager: Exposed socket globally as window.socket"),this.setupGitBranchListener())}updateInitialConnectionStatus(){console.log("SocketManager: Updating initial connection status"),this.socketClient&&"function"==typeof this.socketClient.checkAndUpdateStatus?(console.log("SocketManager: Using socket client checkAndUpdateStatus method"),this.socketClient.checkAndUpdateStatus()):this.socketClient&&this.socketClient.socket?(console.log("SocketManager: Checking socket state directly",{connected:this.socketClient.socket.connected,connecting:this.socketClient.socket.connecting,isConnecting:this.socketClient.isConnecting,isConnected:this.socketClient.isConnected}),this.socketClient.socket.connected?(console.log("SocketManager: Socket is already connected, updating status"),window.socket=this.socketClient.socket,console.log("SocketManager: Exposed socket globally as window.socket"),this.updateConnectionStatus("Connected","connected")):this.socketClient.isConnecting||this.socketClient.socket.connecting?(console.log("SocketManager: Socket is connecting, updating status"),this.updateConnectionStatus("Connecting...","connecting")):(console.log("SocketManager: Socket is disconnected, updating status"),this.updateConnectionStatus("Disconnected","disconnected"))):(console.log("SocketManager: No socket client or socket found, setting disconnected status"),this.updateConnectionStatus("Disconnected","disconnected")),setTimeout(()=>{console.log("SocketManager: Secondary status check after 1 second"),this.socketClient&&this.socketClient.socket&&this.socketClient.socket.connected&&(console.log("SocketManager: Socket connected in secondary check, updating status"),window.socket||(window.socket=this.socketClient.socket,console.log("SocketManager: Exposed socket globally as window.socket (secondary check)")),this.updateConnectionStatus("Connected","connected"))},1e3)}setupGitBranchListener(){this.socketClient.socket.off("git_branch_response"),this.socketClient.socket.on("git_branch_response",t=>{if(t.success){const e=document.getElementById("footer-git-branch");e&&(e.textContent=t.branch||"unknown"),e&&(e.style.display="inline")}else console.error("Git branch request failed:",t.error)})}updateConnectionStatus(t,e){const n=document.getElementById("connection-status");if(n){if(n.querySelector("span")){const e="●";n.innerHTML=`<span>${e}</span> ${t}`}else n.textContent=t;n.className=`status-badge status-${e}`,console.log(`SocketManager: UI updated - status: '${t}' (${e})`)}else console.error("SocketManager: Could not find connection-status element in DOM")}connect(t){this.socketClient&&this.socketClient.connect(t)}disconnect(){this.socketClient&&this.socketClient.disconnect()}isConnected(){return this.socketClient&&this.socketClient.isConnected}isConnecting(){return this.socketClient&&this.socketClient.isConnecting}getSocketClient(){return this.socketClient}getSocket(){return this.socketClient?this.socketClient.socket:null}onConnectionStatusChange(t){this.connectionCallbacks.add(t)}offConnectionStatusChange(t){this.connectionCallbacks.delete(t)}onEventUpdate(t){this.eventUpdateCallbacks.add(t)}offEventUpdate(t){this.eventUpdateCallbacks.delete(t)}toggleConnectionControls(){const t=document.getElementById("connection-controls-row"),e=document.getElementById("connection-toggle-btn");if(t&&e){t.classList.contains("show")?(t.classList.remove("show"),t.style.display="none",e.textContent="Connection Settings"):(t.classList.add("show"),t.style.display="block",e.textContent="Hide Settings")}}setupConnectionControls(){const t=document.getElementById("connect-btn"),e=document.getElementById("disconnect-btn"),n=document.getElementById("connection-toggle-btn");t&&t.addEventListener("click",()=>{const t=document.getElementById("port-input").value||8765;this.connect(t)}),e&&e.addEventListener("click",()=>{this.disconnect()}),n&&n.addEventListener("click",()=>{this.toggleConnectionControls()})}initializeFromURL(t){const e=t.get("port"),n=document.getElementById("port-input");let s=e;s||"http:"!==window.location.protocol||(s=window.location.port||"8765"),s||(s=n?.value||"8765"),n&&(n.value=s);!("false"!==t.get("connect"))||this.isConnected()||this.isConnecting()||(console.log(`SocketManager: Auto-connecting to port ${s}`),this.connect(s))}}class s{constructor(){this.currentTab="events",this.autoScroll=!0,this.selectedCard={tab:null,index:null,type:null,data:null},this.tabNavigation={events:{selectedIndex:-1,items:[]},agents:{selectedIndex:-1,items:[]},tools:{selectedIndex:-1,items:[]},files:{selectedIndex:-1,items:[]}},this.setupEventHandlers(),console.log("UI state manager initialized")}setupEventHandlers(){this.setupTabNavigation(),this.setupUnifiedKeyboardNavigation()}setupTabNavigation(){document.querySelectorAll(".tab-button").forEach(t=>{t.addEventListener("click",()=>{const e=this.getTabNameFromButton(t);this.switchTab(e)})})}setupUnifiedKeyboardNavigation(){document.addEventListener("keydown",t=>{document.activeElement&&["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName)||("ArrowUp"===t.key||"ArrowDown"===t.key?(t.preventDefault(),this.handleUnifiedArrowNavigation("ArrowDown"===t.key?1:-1)):"Enter"===t.key?(t.preventDefault(),this.handleUnifiedEnterKey()):"Escape"===t.key&&this.clearUnifiedSelection())})}getTabNameFromButton(t){const e=t.textContent.toLowerCase();return e.includes("events")?"events":e.includes("activity")?"activity":e.includes("agents")?"agents":e.includes("tools")?"tools":e.includes("files")?"files":e.includes("code")?"code":e.includes("sessions")?"sessions":e.includes("system")?"system":"events"}switchTab(t){console.log(`[DEBUG] switchTab called with tabName: ${t}`);const e=this.currentTab;this.currentTab=t,document.querySelectorAll(".tab-button").forEach(e=>{e.classList.remove("active"),this.getTabNameFromButton(e)===t&&e.classList.add("active")}),document.querySelectorAll(".tab-content").forEach(t=>{t.classList.remove("active")});const n=document.getElementById(`${t}-tab`);n&&n.classList.add("active"),this.clearUnifiedSelection(),document.dispatchEvent(new CustomEvent("tabChanged",{detail:{newTab:t,previousTab:e}})),setTimeout(()=>{this.autoScroll&&this.scrollCurrentTabToBottom()},100)}handleUnifiedArrowNavigation(t){const e=this.tabNavigation[this.currentTab];if(!e)return;let n=e.selectedIndex+t;0!==e.items.length&&(n<0?n=e.items.length-1:n>=e.items.length&&(n=0),this.selectCardByIndex(this.currentTab,n))}handleUnifiedEnterKey(){const t=this.tabNavigation[this.currentTab];if(!t||-1===t.selectedIndex)return;const e=t.items[t.selectedIndex];e&&e.onclick&&e.onclick()}clearUnifiedSelection(){Object.keys(this.tabNavigation).forEach(t=>{this.tabNavigation[t].selectedIndex=-1}),this.clearCardSelection()}updateTabNavigationItems(){const t=this.tabNavigation[this.currentTab];if(!t)return;let e;switch(this.currentTab){case"events":e="#events-list .event-item";break;case"agents":e="#agents-list .event-item";break;case"tools":e="#tools-list .event-item";break;case"files":e="#files-list .event-item"}e&&(t.items=Array.from(document.querySelectorAll(e)))}selectCardByIndex(t,e){const n=this.tabNavigation[t];if(!n||e<0||e>=n.items.length)return;n.selectedIndex=e,this.updateUnifiedSelectionUI();n.items[e]&&this.selectCard(t,e,this.getCardType(t),e),this.showCardDetails(t,e)}updateUnifiedSelectionUI(){document.querySelectorAll(".event-item.keyboard-selected").forEach(t=>{t.classList.remove("keyboard-selected")});const t=this.tabNavigation[this.currentTab];t&&-1!==t.selectedIndex&&t.items[t.selectedIndex]&&t.items[t.selectedIndex].classList.add("keyboard-selected")}showCardDetails(t,e){document.dispatchEvent(new CustomEvent("showCardDetails",{detail:{tabName:t,index:e}}))}selectCard(t,e,n,s){this.clearCardSelection(),this.selectedCard={tab:t,index:e,type:n,data:s},this.updateCardSelectionUI(),console.log("Card selected:",this.selectedCard)}clearCardSelection(){document.querySelectorAll(".event-item.selected, .file-item.selected").forEach(t=>{t.classList.remove("selected")}),this.selectedCard={tab:null,index:null,type:null,data:null}}updateCardSelectionUI(){if(!this.selectedCard.tab||null===this.selectedCard.index)return;let t;switch(this.selectedCard.tab){case"events":t=document.getElementById("events-list");break;case"agents":t=document.getElementById("agents-list");break;case"tools":t=document.getElementById("tools-list");break;case"files":t=document.getElementById("files-list")}if(t){const e=t.querySelectorAll(".event-item, .file-item");e[this.selectedCard.index]&&e[this.selectedCard.index].classList.add("selected")}}getCardType(t){switch(t){case"events":return"event";case"agents":return"agent";case"tools":return"tool";case"files":return"file";default:return"unknown"}}scrollCurrentTabToBottom(){const t=`${this.currentTab}-list`,e=document.getElementById(t);e&&this.autoScroll&&(e.scrollTop=e.scrollHeight)}clearSelection(){this.clearCardSelection(),this.clearUnifiedSelection()}getCurrentTab(){return this.currentTab}getSelectedCard(){return{...this.selectedCard}}getTabNavigation(){return{...this.tabNavigation}}setAutoScroll(t){this.autoScroll=t}getAutoScroll(){return this.autoScroll}}export{n as S,s as U};
|
|
1
|
+
const t=window.io;class e{constructor(){this.socket=null,this.port=null,this.connectionCallbacks={connect:[],disconnect:[],error:[],event:[]},this.eventSchema={required:["source","type","subtype","timestamp","data"],optional:["event","session_id"]},this.isConnected=!1,this.isConnecting=!1,this.lastConnectTime=null,this.disconnectTime=null,this.events=[],this.sessions=new Map,this.currentSessionId=null,this.eventQueue=[],this.maxQueueSize=100,this.retryAttempts=0,this.maxRetryAttempts=5,this.retryDelays=[1e3,2e3,3e3,4e3,5e3],this.pendingEmissions=new Map,this.lastPingTime=null,this.lastPongTime=null,this.pingTimeout=9e4,this.healthCheckInterval=null,this.startStatusCheckFallback(),this.startHealthMonitoring()}connect(t="8765"){this.port=t;const e=`http://localhost:${t}`;if(this.socket&&(this.socket.connected||this.socket.connecting))return console.log("Already connected or connecting, disconnecting first..."),this.socket.disconnect(),void setTimeout(()=>this.doConnect(e),100);this.doConnect(e)}doConnect(e){if(console.log(`Connecting to Socket.IO server at ${e}`),void 0===t)return console.error("Socket.IO library not loaded! Make sure socket.io.min.js is loaded before this script."),void this.notifyConnectionStatus("Socket.IO library not loaded","error");this.isConnecting=!0,this.notifyConnectionStatus("Connecting...","connecting"),this.socket=t(e,{autoConnect:!0,reconnection:!0,reconnectionDelay:1e3,reconnectionDelayMax:5e3,reconnectionAttempts:5,timeout:2e4,forceNew:!0,transports:["websocket","polling"],pingInterval:45e3,pingTimeout:2e4}),this.setupSocketHandlers()}setupSocketHandlers(){this.socket.on("connect",()=>{console.log("Connected to Socket.IO server");const t=this.isConnected;if(this.isConnected=!0,this.isConnecting=!1,this.lastConnectTime=Date.now(),this.retryAttempts=0,this.disconnectTime&&!1===t){const t=(Date.now()-this.disconnectTime)/1e3;console.log(`Reconnected after ${t.toFixed(1)}s downtime`),this.flushEventQueue()}this.notifyConnectionStatus("Connected","connected"),window.socket=this.socket,console.log("SocketClient: Exposed socket globally as window.socket"),this.connectionCallbacks.connect.forEach(t=>t(this.socket.id)),this.requestStatus()}),this.socket.on("disconnect",t=>{const e={reason:t,timestamp:(new Date).toISOString(),wasConnected:this.isConnected,uptimeSeconds:this.lastConnectTime?((Date.now()-this.lastConnectTime)/1e3).toFixed(1):0,lastPing:this.lastPingTime?((Date.now()-this.lastPingTime)/1e3).toFixed(1)+"s ago":"never",lastPong:this.lastPongTime?((Date.now()-this.lastPongTime)/1e3).toFixed(1)+"s ago":"never"};console.log("Disconnected from server:",e),this.isConnected=!1,this.isConnecting=!1,this.disconnectTime=Date.now(),this.notifyConnectionStatus(`Disconnected: ${t}`,"disconnected"),this.connectionCallbacks.disconnect.forEach(e=>e(t));["transport close","ping timeout","transport error","io server disconnect"].includes(t)?(console.log(`Auto-reconnect triggered for reason: ${t}`),this.scheduleReconnect()):"io client disconnect"===t?console.log("Client-initiated disconnect, not auto-reconnecting"):(console.log(`Unknown disconnect reason: ${t}, attempting reconnect anyway`),this.scheduleReconnect())}),this.socket.on("connect_error",t=>{console.error("Connection error:",t),this.isConnecting=!1;const e=t.message||t.description||"Unknown error";this.notifyConnectionStatus(`Connection Error: ${e}`,"disconnected"),this.addEvent({type:"connection.error",timestamp:(new Date).toISOString(),data:{error:e,url:this.socket.io.uri,retry_attempt:this.retryAttempts}}),this.connectionCallbacks.error.forEach(t=>t(e)),this.scheduleReconnect()}),this.socket.on("claude_event",t=>{console.log("Received claude_event:",t);const e=this.validateEventSchema(t);if(!e)return void console.warn("Invalid event schema received:",t);e.type&&e.type.startsWith("code:")&&console.log("Code analysis event received via claude_event, adding to events list for troubleshooting:",e.type);const n=this.transformEvent(e);console.log("Transformed event:",n),this.addEvent(n)}),this.socket.on("ping",t=>{this.lastPingTime=Date.now(),this.socket.emit("pong",{timestamp:t.timestamp,client_time:Date.now()})}),this.socket.on("pong",t=>{this.lastPongTime=Date.now()}),this.socket.on("session.started",t=>{this.addEvent({type:"session",subtype:"started",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("session.ended",t=>{this.addEvent({type:"session",subtype:"ended",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("claude.request",t=>{this.addEvent({type:"claude",subtype:"request",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("claude.response",t=>{this.addEvent({type:"claude",subtype:"response",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("agent.loaded",t=>{this.addEvent({type:"agent",subtype:"loaded",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("agent.executed",t=>{this.addEvent({type:"agent",subtype:"executed",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("hook.pre",t=>{this.addEvent({type:"hook",subtype:"pre",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("hook.post",t=>{this.addEvent({type:"hook",subtype:"post",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("todo.updated",t=>{this.addEvent({type:"todo",subtype:"updated",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("memory.operation",t=>{this.addEvent({type:"memory",subtype:"operation",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("log.entry",t=>{this.addEvent({type:"log",subtype:"entry",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("code:analysis:queued",t=>{console.log("Code analysis queued event received, adding to events list for troubleshooting"),this.addEvent({type:"code",subtype:"analysis:queued",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("code:analysis:accepted",t=>{console.log("Code analysis accepted event received, adding to events list for troubleshooting"),this.addEvent({type:"code",subtype:"analysis:accepted",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("code:analysis:start",t=>{console.log("Code analysis start event received, adding to events list for troubleshooting"),this.addEvent({type:"code",subtype:"analysis:start",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("code:analysis:complete",t=>{console.log("Code analysis complete event received, adding to events list for troubleshooting"),this.addEvent({type:"code",subtype:"analysis:complete",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("code:analysis:error",t=>{console.log("Code analysis error event received, adding to events list for troubleshooting"),this.addEvent({type:"code",subtype:"analysis:error",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("code:file:start",t=>{console.log("Code file start event received, adding to events list for troubleshooting"),this.addEvent({type:"code",subtype:"file:start",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("code:node:found",t=>{console.log("Code node found event received, adding to events list for troubleshooting"),this.addEvent({type:"code",subtype:"node:found",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("code:analysis:progress",t=>{console.log("Code analysis progress event received, adding to events list for troubleshooting"),this.addEvent({type:"code",subtype:"analysis:progress",timestamp:(new Date).toISOString(),data:t})}),this.socket.on("history",t=>{console.log("Received event history:",t),t&&Array.isArray(t.events)?(console.log(`Processing ${t.events.length} historical events (${t.count} sent, ${t.total_available} total available)`),t.events.forEach(t=>{const e=this.transformEvent(t);this.addEvent(e,!1)}),this.notifyEventUpdate(),console.log(`Event history loaded: ${t.events.length} events added to dashboard`)):Array.isArray(t)&&(console.log("Received legacy event history format:",t.length,"events"),t.forEach(t=>{const e=this.transformEvent(t);this.addEvent(e,!1)}),this.notifyEventUpdate())}),this.socket.on("system.status",t=>{console.log("Received system status:",t),t.sessions&&this.updateSessions(t.sessions),t.current_session&&(this.currentSessionId=t.current_session)})}disconnect(){this.socket&&(this.socket.disconnect(),this.socket=null),this.port=null,this.isConnected=!1,this.isConnecting=!1}emitWithRetry(t,e=null,n={}){const{maxRetries:s=3,retryDelays:o=[1e3,2e3,4e3],onSuccess:i=null,onFailure:a=null}=n,c=`${t}_${Date.now()}_${Math.random()}`,l=(n=0)=>{if(this.socket&&this.socket.connected)try{this.socket.emit(t,e),console.log(`Emitted ${t} successfully`),this.pendingEmissions.delete(c),i&&i()}catch(r){if(console.error(`Failed to emit ${t} (attempt ${n+1}):`,r),n<s-1){const s=o[n]||o[o.length-1];console.log(`Retrying ${t} in ${s}ms...`),this.pendingEmissions.set(c,{event:t,data:e,attemptNum:n+1,scheduledTime:Date.now()+s}),setTimeout(()=>l(n+1),s)}else console.error(`Failed to emit ${t} after ${s} attempts`),this.pendingEmissions.delete(c),a&&a("max_retries_exceeded")}else 0===n&&(this.queueEvent(t,e),console.log(`Queued ${t} for later emission (disconnected)`),a&&a("disconnected"))};l()}queueEvent(t,e){if(this.eventQueue.length>=this.maxQueueSize){const t=this.eventQueue.shift();console.warn(`Event queue full, dropped oldest event: ${t.event}`)}this.eventQueue.push({event:t,data:e,timestamp:Date.now()})}flushEventQueue(){if(0===this.eventQueue.length)return;console.log(`Flushing ${this.eventQueue.length} queued events...`);const t=[...this.eventQueue];this.eventQueue=[],t.forEach((t,e)=>{setTimeout(()=>{this.socket&&this.socket.connected&&(this.socket.emit(t.event,t.data),console.log(`Flushed queued event: ${t.event}`))},100*e)})}scheduleReconnect(){if(this.retryAttempts>=this.maxRetryAttempts)return console.log("Max reconnection attempts reached, stopping auto-reconnect"),void this.notifyConnectionStatus("Reconnection failed","disconnected");const t=this.retryDelays[this.retryAttempts]||this.retryDelays[this.retryDelays.length-1];this.retryAttempts++,console.log(`Scheduling reconnect attempt ${this.retryAttempts}/${this.maxRetryAttempts} in ${t}ms...`),this.notifyConnectionStatus(`Reconnecting in ${t/1e3}s...`,"connecting"),setTimeout(()=>{!this.isConnected&&this.port&&(console.log(`Attempting reconnection ${this.retryAttempts}/${this.maxRetryAttempts}...`),this.connect(this.port))},t)}requestStatus(){this.socket&&this.socket.connected&&(console.log("Requesting server status..."),this.emitWithRetry("request.status",null,{maxRetries:2,retryDelays:[500,1e3]}))}requestHistory(t={}){if(this.socket&&this.socket.connected){const e={limit:t.limit||50,event_types:t.event_types||[]};console.log("Requesting event history...",e),this.emitWithRetry("get_history",e,{maxRetries:3,retryDelays:[1e3,2e3,3e3],onFailure:t=>{console.error(`Failed to request history: ${t}`)}})}else console.warn("Cannot request history: not connected to server")}addEvent(t,e=!0){if(t.timestamp||(t.timestamp=(new Date).toISOString()),t.id||(t.id=Date.now()+Math.random()),this.events.push(t),t.data&&t.data.session_id){const e=t.data.session_id;this.sessions.has(e)||this.sessions.set(e,{id:e,startTime:t.timestamp,lastActivity:t.timestamp,eventCount:0,working_directory:null,git_branch:null});const n=this.sessions.get(e);n.lastActivity=t.timestamp,n.eventCount++;const s=[t.data.cwd,t.data.working_directory,t.data.working_dir,t.data.workingDirectory,t.data.instance_info?.working_dir,t.data.instance_info?.working_directory,t.data.instance_info?.cwd,t.cwd,t.working_directory,t.working_dir];for(const t of s)if(t&&"string"==typeof t&&t.trim()){n.working_directory=t,console.log(`[SOCKET-CLIENT] Found working directory for session ${e}:`,t);break}t.data.git_branch?n.git_branch=t.data.git_branch:t.data.instance_info&&t.data.instance_info.git_branch&&(n.git_branch=t.data.instance_info.git_branch)}e&&this.notifyEventUpdate()}updateSessions(t){Array.isArray(t)&&t.forEach(t=>{this.sessions.set(t.id,t)})}clearEvents(){this.events=[],this.sessions.clear(),this.notifyEventUpdate()}refreshHistory(t={}){this.clearEvents(),this.requestHistory(t)}getEventsBySession(t=null){return t?this.events.filter(e=>e.data&&e.data.session_id===t):this.events}onConnection(t,e){this.connectionCallbacks[t]&&this.connectionCallbacks[t].push(e)}onEventUpdate(t){this.connectionCallbacks.event.push(t)}on(t,e){if(this.socket)return this.socket.on(t,e);console.warn(`Cannot subscribe to '${t}': socket not initialized`)}off(t,e){if(this.socket)return this.socket.off(t,e);console.warn(`Cannot unsubscribe from '${t}': socket not initialized`)}notifyConnectionStatus(t,e){console.log(`SocketClient: Connection status changed to '${t}' (${e})`),this.updateConnectionStatusDOM(t,e),document.dispatchEvent(new CustomEvent("socketConnectionStatus",{detail:{status:t,type:e}}))}updateConnectionStatusDOM(t,e){const n=document.getElementById("connection-status");n?(n.innerHTML=`<span>●</span> ${t}`,n.className=`status-badge status-${e}`,console.log(`SocketClient: Direct DOM update - status: '${t}' (${e})`)):console.warn("SocketClient: Could not find connection-status element in DOM")}notifyEventUpdate(){this.connectionCallbacks.event.forEach(t=>t(this.events,this.sessions)),document.dispatchEvent(new CustomEvent("socketEventUpdate",{detail:{events:this.events,sessions:this.sessions}}))}getConnectionState(){return{isConnected:this.isConnected,isConnecting:this.isConnecting,socketId:this.socket?this.socket.id:null}}validateEventSchema(t){if(!t||"object"!=typeof t)return console.warn("Event data is not an object:",t),null;const e={...t};return e.source||(e.source="system"),e.type||(e.event?e.type=e.event:e.type="unknown"),e.subtype||(e.subtype="generic"),e.timestamp||(e.timestamp=(new Date).toISOString()),e.data||(e.data={}),e.data&&"object"!=typeof e.data&&(e.data={value:e.data}),console.log("Validated event:",e),e}transformEvent(t){if(!t)return t;let e={...t};if(t.type&&t.subtype&&!t.type.includes(".")&&!t.type.includes(":"))e.originalEventName||("generic"===t.subtype||t.type===t.subtype?e.originalEventName=t.type:e.originalEventName=`${t.type}.${t.subtype}`);else if(!t.type&&t.event){const n=t.event;"TestStart"===n||"TestEnd"===n?(e.type="test",e.subtype=n.toLowerCase().replace("test","")):"SubagentStart"===n||"SubagentStop"===n?(e.type="subagent",e.subtype=n.toLowerCase().replace("subagent","")):"ToolCall"===n?(e.type="tool",e.subtype="call"):"UserPrompt"===n?(e.type="hook",e.subtype="user_prompt"):(e.type="unknown",e.subtype=n.toLowerCase(),e.type===e.subtype&&(e.subtype="event")),delete e.event,e.originalEventName=n}else if(t.type){const n=t.type;if(n.startsWith("hook.")){const t=n.substring(5);e.type="hook",e.subtype=t,e.originalEventName=n}else if(n.startsWith("code:")){e.type="code";const t=n.substring(5);e.subtype=t.replace(/:/g,"_"),e.originalEventName=n}else if(n.includes(".")){const[t,...s]=n.split(".");e.type=t,e.subtype=s.join("."),e.originalEventName=n}else if(n.includes(":")){const t=n.split(":",2);e.type=t[0],e.subtype=t.length>1?t[1].replace(/:/g,"_"):"generic",e.originalEventName=n}else t.subtype||(e.subtype="generic",e.originalEventName=n)}else e.type="unknown",e.subtype="",e.originalEventName="unknown";if(t.data&&"object"==typeof t.data){const n=["type","subtype","timestamp","id","event","event_type","originalEventName"];Object.keys(t.data).forEach(s=>{n.includes(s)?console.warn(`Protected field '${s}' in data object was not copied to top level to preserve event structure`):"tool_parameters"===s&&"object"==typeof t.data[s]?e[s]=JSON.parse(JSON.stringify(t.data[s])):e[s]=t.data[s]}),e.data=t.data}if("hook"===e.type?"pre_tool"===e.subtype?e.hook_event_name="PreToolUse":"post_tool"===e.subtype?e.hook_event_name="PostToolUse":"subagent_start"===e.subtype?e.hook_event_name="SubagentStart":"subagent_stop"===e.subtype?e.hook_event_name="SubagentStop":"todo_write"===e.subtype?e.hook_event_name="TodoWrite":"start"===e.subtype?e.hook_event_name="Start":"stop"===e.subtype&&(e.hook_event_name="Stop"):"subagent"===e.type?"start"===e.subtype?e.hook_event_name="SubagentStart":"stop"===e.subtype&&(e.hook_event_name="SubagentStop"):"todo"===e.type&&"updated"===e.subtype&&(e.hook_event_name="TodoWrite"),"hook"===e.type&&("pre_tool"===e.subtype||"post_tool"===e.subtype)){console.log("Transformed tool event:",{type:e.type,subtype:e.subtype,hook_event_name:e.hook_event_name,tool_name:e.tool_name,has_tool_parameters:!!e.tool_parameters,tool_parameters:e.tool_parameters,has_data:!!e.data,keys:Object.keys(e).filter(t=>"data"!==t)});["Read","Write","Edit","MultiEdit","NotebookEdit"].includes(e.tool_name)&&console.log("File tool event details:",{tool_name:e.tool_name,file_path:e.tool_parameters?.file_path,path:e.tool_parameters?.path,notebook_path:e.tool_parameters?.notebook_path,full_parameters:e.tool_parameters})}return e}getState(){return{events:this.events,sessions:this.sessions,currentSessionId:this.currentSessionId}}startHealthMonitoring(){this.healthCheckInterval=setInterval(()=>{if(this.isConnected&&this.lastPingTime){const t=Date.now()-this.lastPingTime;t>this.pingTimeout&&(console.warn(`No ping from server for ${t/1e3}s, connection may be stale`),this.socket&&(console.log("Forcing reconnection due to stale connection..."),this.socket.disconnect(),setTimeout(()=>{this.port&&this.connect(this.port)},1e3)))}},1e4)}stopHealthMonitoring(){this.healthCheckInterval&&(clearInterval(this.healthCheckInterval),this.healthCheckInterval=null)}startStatusCheckFallback(){setInterval(()=>{this.checkAndUpdateStatus()},2e3),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{setTimeout(()=>this.checkAndUpdateStatus(),100)}):setTimeout(()=>this.checkAndUpdateStatus(),100)}checkAndUpdateStatus(){let t="Disconnected",e="disconnected";this.socket&&(this.socket.connected?(t="Connected",e="connected",this.isConnected=!0,this.isConnecting=!1):this.socket.connecting||this.isConnecting?(t="Connecting...",e="connecting",this.isConnected=!1):(t="Disconnected",e="disconnected",this.isConnected=!1,this.isConnecting=!1));const n=document.getElementById("connection-status");if(n){const s=n.textContent.replace("●","").trim(),o=n.className,i=`status-badge status-${e}`;s===t&&o===i||(console.log(`SocketClient: Fallback update - was '${s}' (${o}), now '${t}' (${i})`),this.updateConnectionStatusDOM(t,e))}}destroy(){this.stopHealthMonitoring(),this.socket&&(this.socket.disconnect(),this.socket=null),this.eventQueue=[],this.pendingEmissions.clear()}getConnectionMetrics(){return{isConnected:this.isConnected,uptime:this.lastConnectTime?(Date.now()-this.lastConnectTime)/1e3:0,lastPing:this.lastPingTime?(Date.now()-this.lastPingTime)/1e3:null,queuedEvents:this.eventQueue.length,pendingEmissions:this.pendingEmissions.size,retryAttempts:this.retryAttempts}}}window.SocketClient=e;class n{constructor(){this.socketClient=null,this.connectionCallbacks=new Set,this.eventUpdateCallbacks=new Set,this.socketClient=new e,window.socketClient=this.socketClient,this.setupSocketEventHandlers(),setTimeout(()=>{this.updateInitialConnectionStatus()},100),console.log("Socket manager initialized")}setupSocketEventHandlers(){document.addEventListener("socketConnectionStatus",t=>{console.log(`SocketManager: Processing connection status update: ${t.detail.status} (${t.detail.type})`),this.handleConnectionStatusChange(t.detail.status,t.detail.type),this.connectionCallbacks.forEach(e=>{try{e(t.detail.status,t.detail.type)}catch(n){console.error("Error in connection callback:",n)}})}),this.socketClient&&this.socketClient.onEventUpdate(t=>{this.eventUpdateCallbacks.forEach(e=>{try{e(t)}catch(n){console.error("Error in event update callback:",n)}})})}handleConnectionStatusChange(t,e){this.updateConnectionStatus(t,e),"connected"===e&&this.socketClient&&this.socketClient.socket&&(window.socket=this.socketClient.socket,console.log("SocketManager: Exposed socket globally as window.socket"),this.setupGitBranchListener())}updateInitialConnectionStatus(){console.log("SocketManager: Updating initial connection status"),this.socketClient&&"function"==typeof this.socketClient.checkAndUpdateStatus?(console.log("SocketManager: Using socket client checkAndUpdateStatus method"),this.socketClient.checkAndUpdateStatus()):this.socketClient&&this.socketClient.socket?(console.log("SocketManager: Checking socket state directly",{connected:this.socketClient.socket.connected,connecting:this.socketClient.socket.connecting,isConnecting:this.socketClient.isConnecting,isConnected:this.socketClient.isConnected}),this.socketClient.socket.connected?(console.log("SocketManager: Socket is already connected, updating status"),window.socket=this.socketClient.socket,console.log("SocketManager: Exposed socket globally as window.socket"),this.updateConnectionStatus("Connected","connected")):this.socketClient.isConnecting||this.socketClient.socket.connecting?(console.log("SocketManager: Socket is connecting, updating status"),this.updateConnectionStatus("Connecting...","connecting")):(console.log("SocketManager: Socket is disconnected, updating status"),this.updateConnectionStatus("Disconnected","disconnected"))):(console.log("SocketManager: No socket client or socket found, setting disconnected status"),this.updateConnectionStatus("Disconnected","disconnected")),setTimeout(()=>{console.log("SocketManager: Secondary status check after 1 second"),this.socketClient&&this.socketClient.socket&&this.socketClient.socket.connected&&(console.log("SocketManager: Socket connected in secondary check, updating status"),window.socket||(window.socket=this.socketClient.socket,console.log("SocketManager: Exposed socket globally as window.socket (secondary check)")),this.updateConnectionStatus("Connected","connected"))},1e3)}setupGitBranchListener(){this.socketClient.socket.off("git_branch_response"),this.socketClient.socket.on("git_branch_response",t=>{if(t.success){const e=document.getElementById("footer-git-branch");e&&(e.textContent=t.branch||"unknown"),e&&(e.style.display="inline")}else console.error("Git branch request failed:",t.error)})}updateConnectionStatus(t,e){const n=document.getElementById("connection-status");if(n){if(n.querySelector("span")){const e="●";n.innerHTML=`<span>${e}</span> ${t}`}else n.textContent=t;n.className=`status-badge status-${e}`,console.log(`SocketManager: UI updated - status: '${t}' (${e})`)}else console.error("SocketManager: Could not find connection-status element in DOM")}connect(t){this.socketClient&&this.socketClient.connect(t)}disconnect(){this.socketClient&&this.socketClient.disconnect()}isConnected(){return this.socketClient&&this.socketClient.isConnected}isConnecting(){return this.socketClient&&this.socketClient.isConnecting}getSocketClient(){return this.socketClient}getSocket(){return this.socketClient?this.socketClient.socket:null}onConnectionStatusChange(t){this.connectionCallbacks.add(t)}offConnectionStatusChange(t){this.connectionCallbacks.delete(t)}onEventUpdate(t){this.eventUpdateCallbacks.add(t)}offEventUpdate(t){this.eventUpdateCallbacks.delete(t)}toggleConnectionControls(){const t=document.getElementById("connection-controls-row"),e=document.getElementById("connection-toggle-btn");if(t&&e){t.classList.contains("show")?(t.classList.remove("show"),t.style.display="none",e.textContent="Connection Settings"):(t.classList.add("show"),t.style.display="block",e.textContent="Hide Settings")}}setupConnectionControls(){const t=document.getElementById("connect-btn"),e=document.getElementById("disconnect-btn"),n=document.getElementById("connection-toggle-btn");t&&t.addEventListener("click",()=>{const t=document.getElementById("port-input").value||8765;this.connect(t)}),e&&e.addEventListener("click",()=>{this.disconnect()}),n&&n.addEventListener("click",()=>{this.toggleConnectionControls()})}initializeFromURL(t){const e=t.get("port"),n=document.getElementById("port-input");let s=e;s||"http:"!==window.location.protocol||(s=window.location.port||"8765"),s||(s=n?.value||"8765"),n&&(n.value=s);!("false"!==t.get("connect"))||this.isConnected()||this.isConnecting()||(console.log(`SocketManager: Auto-connecting to port ${s}`),this.connect(s))}}class s{constructor(){this.currentTab="events",this.autoScroll=!0,this.selectedCard={tab:null,index:null,type:null,data:null},this.tabNavigation={events:{selectedIndex:-1,items:[]},agents:{selectedIndex:-1,items:[]},tools:{selectedIndex:-1,items:[]},files:{selectedIndex:-1,items:[]}},this.setupEventHandlers(),console.log("UI state manager initialized")}setupEventHandlers(){this.setupTabNavigation(),this.setupUnifiedKeyboardNavigation()}setupTabNavigation(){document.querySelectorAll(".tab-button").forEach(t=>{t.addEventListener("click",()=>{const e=this.getTabNameFromButton(t);this.switchTab(e)})})}setupUnifiedKeyboardNavigation(){document.addEventListener("keydown",t=>{document.activeElement&&["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName)||("ArrowUp"===t.key||"ArrowDown"===t.key?(t.preventDefault(),this.handleUnifiedArrowNavigation("ArrowDown"===t.key?1:-1)):"Enter"===t.key?(t.preventDefault(),this.handleUnifiedEnterKey()):"Escape"===t.key&&this.clearUnifiedSelection())})}getTabNameFromButton(t){const e=t.textContent.toLowerCase();return e.includes("events")?"events":e.includes("activity")?"activity":e.includes("agents")?"agents":e.includes("tools")?"tools":e.includes("files")?"files":e.includes("code")?"code":e.includes("sessions")?"sessions":e.includes("system")?"system":"events"}switchTab(t){console.log(`[DEBUG] switchTab called with tabName: ${t}`);const e=this.currentTab;this.currentTab=t,document.querySelectorAll(".tab-button").forEach(e=>{e.classList.remove("active"),this.getTabNameFromButton(e)===t&&e.classList.add("active")}),document.querySelectorAll(".tab-content").forEach(t=>{t.classList.remove("active")});const n=document.getElementById(`${t}-tab`);n&&n.classList.add("active"),this.clearUnifiedSelection(),document.dispatchEvent(new CustomEvent("tabChanged",{detail:{newTab:t,previousTab:e}})),setTimeout(()=>{this.autoScroll&&this.scrollCurrentTabToBottom()},100)}handleUnifiedArrowNavigation(t){const e=this.tabNavigation[this.currentTab];if(!e)return;let n=e.selectedIndex+t;0!==e.items.length&&(n<0?n=e.items.length-1:n>=e.items.length&&(n=0),this.selectCardByIndex(this.currentTab,n))}handleUnifiedEnterKey(){const t=this.tabNavigation[this.currentTab];if(!t||-1===t.selectedIndex)return;const e=t.items[t.selectedIndex];e&&e.onclick&&e.onclick()}clearUnifiedSelection(){Object.keys(this.tabNavigation).forEach(t=>{this.tabNavigation[t].selectedIndex=-1}),this.clearCardSelection()}updateTabNavigationItems(){const t=this.tabNavigation[this.currentTab];if(!t)return;let e;switch(this.currentTab){case"events":e="#events-list .event-item";break;case"agents":e="#agents-list .event-item";break;case"tools":e="#tools-list .event-item";break;case"files":e="#files-list .event-item"}e&&(t.items=Array.from(document.querySelectorAll(e)))}selectCardByIndex(t,e){const n=this.tabNavigation[t];if(!n||e<0||e>=n.items.length)return;n.selectedIndex=e,this.updateUnifiedSelectionUI();n.items[e]&&this.selectCard(t,e,this.getCardType(t),e),this.showCardDetails(t,e)}updateUnifiedSelectionUI(){document.querySelectorAll(".event-item.keyboard-selected").forEach(t=>{t.classList.remove("keyboard-selected")});const t=this.tabNavigation[this.currentTab];t&&-1!==t.selectedIndex&&t.items[t.selectedIndex]&&t.items[t.selectedIndex].classList.add("keyboard-selected")}showCardDetails(t,e){document.dispatchEvent(new CustomEvent("showCardDetails",{detail:{tabName:t,index:e}}))}selectCard(t,e,n,s){this.clearCardSelection(),this.selectedCard={tab:t,index:e,type:n,data:s},this.updateCardSelectionUI(),console.log("Card selected:",this.selectedCard)}clearCardSelection(){document.querySelectorAll(".event-item.selected, .file-item.selected").forEach(t=>{t.classList.remove("selected")}),this.selectedCard={tab:null,index:null,type:null,data:null}}updateCardSelectionUI(){if(!this.selectedCard.tab||null===this.selectedCard.index)return;let t;switch(this.selectedCard.tab){case"events":t=document.getElementById("events-list");break;case"agents":t=document.getElementById("agents-list");break;case"tools":t=document.getElementById("tools-list");break;case"files":t=document.getElementById("files-list")}if(t){const e=t.querySelectorAll(".event-item, .file-item");e[this.selectedCard.index]&&e[this.selectedCard.index].classList.add("selected")}}getCardType(t){switch(t){case"events":return"event";case"agents":return"agent";case"tools":return"tool";case"files":return"file";default:return"unknown"}}scrollCurrentTabToBottom(){const t=`${this.currentTab}-list`,e=document.getElementById(t);e&&this.autoScroll&&(e.scrollTop=e.scrollHeight)}clearSelection(){this.clearCardSelection(),this.clearUnifiedSelection()}getCurrentTab(){return this.currentTab}getSelectedCard(){return{...this.selectedCard}}getTabNavigation(){return{...this.tabNavigation}}setAutoScroll(t){this.autoScroll=t}getAutoScroll(){return this.autoScroll}}export{n as S,s as U};
|
|
2
2
|
//# sourceMappingURL=socket-client.js.map
|
|
@@ -51,6 +51,12 @@ class ActivityTree {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
// Clear any existing text content that might be in the container
|
|
55
|
+
if (this.container.textContent && this.container.textContent.trim()) {
|
|
56
|
+
console.log('Clearing existing text content from container:', this.container.textContent);
|
|
57
|
+
this.container.textContent = '';
|
|
58
|
+
}
|
|
59
|
+
|
|
54
60
|
console.log('Activity tree container found:', this.container);
|
|
55
61
|
|
|
56
62
|
// Check if the container is visible before initializing
|
|
@@ -63,6 +69,10 @@ class ActivityTree {
|
|
|
63
69
|
// Initialize even if tab is not active, but don't render until visible
|
|
64
70
|
if (!tabPanel.classList.contains('active')) {
|
|
65
71
|
console.log('Activity tab not active, initializing but deferring render');
|
|
72
|
+
// Clear any text content that might be showing
|
|
73
|
+
if (this.container.textContent && this.container.textContent.trim()) {
|
|
74
|
+
this.container.textContent = '';
|
|
75
|
+
}
|
|
66
76
|
// Set up basic structure but defer visualization
|
|
67
77
|
this.setupControls();
|
|
68
78
|
this.initializeTreeData();
|
|
@@ -71,28 +81,97 @@ class ActivityTree {
|
|
|
71
81
|
return;
|
|
72
82
|
}
|
|
73
83
|
|
|
84
|
+
// Clear container before creating visualization
|
|
85
|
+
if (this.container.textContent && this.container.textContent.trim()) {
|
|
86
|
+
console.log('Clearing container text before creating visualization');
|
|
87
|
+
this.container.textContent = '';
|
|
88
|
+
}
|
|
89
|
+
|
|
74
90
|
this.setupControls();
|
|
75
91
|
this.createVisualization();
|
|
76
92
|
|
|
77
93
|
if (!this.svg || !this.treeGroup) {
|
|
78
94
|
console.error('Failed to create D3 visualization elements');
|
|
95
|
+
// Show error message in container
|
|
96
|
+
if (this.container) {
|
|
97
|
+
this.container.innerHTML = '<div style="padding: 20px; text-align: center; color: #e53e3e;">⚠️ Failed to create visualization. Please refresh the page.</div>';
|
|
98
|
+
}
|
|
79
99
|
return;
|
|
80
100
|
}
|
|
81
101
|
|
|
82
102
|
this.initializeTreeData();
|
|
83
|
-
|
|
103
|
+
|
|
104
|
+
// Only update if we have a valid root
|
|
105
|
+
if (this.root) {
|
|
106
|
+
this.update(this.root);
|
|
107
|
+
} else {
|
|
108
|
+
console.warn('Root not created, skipping initial update');
|
|
109
|
+
}
|
|
110
|
+
|
|
84
111
|
this.subscribeToEvents();
|
|
85
112
|
|
|
86
113
|
this.initialized = true;
|
|
87
114
|
console.log('Activity tree initialization complete');
|
|
88
115
|
}
|
|
89
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Force show the tree visualization
|
|
119
|
+
*/
|
|
120
|
+
forceShow() {
|
|
121
|
+
console.log('ActivityTree.forceShow() called');
|
|
122
|
+
|
|
123
|
+
// Ensure container is available
|
|
124
|
+
if (!this.container) {
|
|
125
|
+
this.container = document.getElementById('activity-tree-container') || document.getElementById('activity-tree');
|
|
126
|
+
if (!this.container) {
|
|
127
|
+
console.error('Cannot find activity tree container');
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Clear any text content
|
|
133
|
+
if (this.container.textContent && this.container.textContent.trim()) {
|
|
134
|
+
console.log('Clearing text from container:', this.container.textContent);
|
|
135
|
+
this.container.innerHTML = '';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Create visualization if needed
|
|
139
|
+
if (!this.svg) {
|
|
140
|
+
this.createVisualization();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Initialize tree data if needed
|
|
144
|
+
if (!this.root) {
|
|
145
|
+
this.initializeTreeData();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Update the tree
|
|
149
|
+
if (this.root && this.svg && this.treeGroup) {
|
|
150
|
+
this.update(this.root);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Ensure the SVG is visible
|
|
154
|
+
if (this.svg) {
|
|
155
|
+
const svgNode = this.svg.node();
|
|
156
|
+
if (svgNode) {
|
|
157
|
+
svgNode.style.display = 'block';
|
|
158
|
+
svgNode.style.visibility = 'visible';
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
90
163
|
/**
|
|
91
164
|
* Render the visualization when tab becomes visible (called when switching to Activity tab)
|
|
92
165
|
*/
|
|
93
166
|
renderWhenVisible() {
|
|
94
167
|
console.log('ActivityTree.renderWhenVisible() called');
|
|
95
168
|
|
|
169
|
+
// Ensure the container is clean
|
|
170
|
+
if (this.container && this.container.textContent && this.container.textContent.trim() && !this.svg) {
|
|
171
|
+
console.log('Clearing text content before rendering:', this.container.textContent);
|
|
172
|
+
this.container.textContent = '';
|
|
173
|
+
}
|
|
174
|
+
|
|
96
175
|
if (!this.initialized) {
|
|
97
176
|
console.log('Not initialized yet, calling initialize...');
|
|
98
177
|
this.initialize();
|
|
@@ -103,8 +182,14 @@ class ActivityTree {
|
|
|
103
182
|
if (!this.svg) {
|
|
104
183
|
console.log('Creating deferred visualization...');
|
|
105
184
|
this.createVisualization();
|
|
106
|
-
if (this.svg && this.treeGroup) {
|
|
185
|
+
if (this.svg && this.treeGroup && this.root) {
|
|
107
186
|
this.update(this.root);
|
|
187
|
+
} else if (!this.root) {
|
|
188
|
+
console.warn('No root node available, initializing tree data...');
|
|
189
|
+
this.initializeTreeData();
|
|
190
|
+
if (this.root && this.svg && this.treeGroup) {
|
|
191
|
+
this.update(this.root);
|
|
192
|
+
}
|
|
108
193
|
}
|
|
109
194
|
}
|
|
110
195
|
|
|
@@ -112,6 +197,12 @@ class ActivityTree {
|
|
|
112
197
|
if (this.root && this.svg) {
|
|
113
198
|
console.log('Updating tree with current data...');
|
|
114
199
|
this.update(this.root);
|
|
200
|
+
} else {
|
|
201
|
+
console.warn('Cannot update tree - missing components:', {
|
|
202
|
+
hasRoot: !!this.root,
|
|
203
|
+
hasSvg: !!this.svg,
|
|
204
|
+
hasTreeGroup: !!this.treeGroup
|
|
205
|
+
});
|
|
115
206
|
}
|
|
116
207
|
}
|
|
117
208
|
|
|
@@ -163,6 +254,10 @@ class ActivityTree {
|
|
|
163
254
|
// Check if D3 is available
|
|
164
255
|
if (typeof d3 === 'undefined') {
|
|
165
256
|
console.error('D3.js is not loaded! Cannot create activity tree visualization.');
|
|
257
|
+
// Try to display an error message in the container
|
|
258
|
+
if (this.container) {
|
|
259
|
+
this.container.innerHTML = '<div style="padding: 20px; text-align: center; color: #e53e3e;">⚠️ D3.js is not loaded. Cannot create visualization.</div>';
|
|
260
|
+
}
|
|
166
261
|
return;
|
|
167
262
|
}
|
|
168
263
|
|
|
@@ -173,8 +268,8 @@ class ActivityTree {
|
|
|
173
268
|
|
|
174
269
|
console.log('Creating D3 visualization with dimensions:', { width: this.width, height: this.height });
|
|
175
270
|
|
|
176
|
-
// Clear any existing
|
|
177
|
-
d3.select(this.container).
|
|
271
|
+
// Clear any existing content (including text)
|
|
272
|
+
d3.select(this.container).selectAll('*').remove();
|
|
178
273
|
|
|
179
274
|
// Create SVG
|
|
180
275
|
this.svg = d3.select(this.container)
|
|
@@ -230,6 +325,10 @@ class ActivityTree {
|
|
|
230
325
|
// Check if D3 is available
|
|
231
326
|
if (typeof d3 === 'undefined') {
|
|
232
327
|
console.error('ActivityTree: D3 is not available - cannot create hierarchy!');
|
|
328
|
+
// Try to display an error message
|
|
329
|
+
if (this.container) {
|
|
330
|
+
this.container.innerHTML = '<div style="padding: 20px; text-align: center; color: #e53e3e;">⚠️ Waiting for D3.js to load...</div>';
|
|
331
|
+
}
|
|
233
332
|
return;
|
|
234
333
|
}
|
|
235
334
|
|
|
@@ -238,6 +337,9 @@ class ActivityTree {
|
|
|
238
337
|
this.root.y0 = 0;
|
|
239
338
|
|
|
240
339
|
console.log('ActivityTree: Root node created:', this.root);
|
|
340
|
+
|
|
341
|
+
// Update stats immediately after creating root
|
|
342
|
+
this.updateStats();
|
|
241
343
|
}
|
|
242
344
|
|
|
243
345
|
/**
|
|
@@ -665,23 +767,74 @@ class ActivityTree {
|
|
|
665
767
|
update(source) {
|
|
666
768
|
console.log('ActivityTree: update() called with source:', source);
|
|
667
769
|
|
|
770
|
+
// Check if D3 is available
|
|
771
|
+
if (typeof d3 === 'undefined') {
|
|
772
|
+
console.error('ActivityTree: Cannot update - D3.js not loaded');
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
|
|
668
776
|
// Check if visualization is ready
|
|
669
777
|
if (!this.svg || !this.treeGroup) {
|
|
670
778
|
console.warn('ActivityTree: Cannot update - SVG not initialized');
|
|
671
|
-
|
|
779
|
+
// Try to create visualization if container exists
|
|
780
|
+
if (this.container) {
|
|
781
|
+
console.log('Attempting to create visualization from update()');
|
|
782
|
+
this.createVisualization();
|
|
783
|
+
// Check again after creation attempt
|
|
784
|
+
if (!this.svg || !this.treeGroup) {
|
|
785
|
+
console.error('Failed to create visualization in update()');
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
} else {
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
672
791
|
}
|
|
673
792
|
|
|
674
793
|
if (!this.treeLayout) {
|
|
675
794
|
console.warn('ActivityTree: Cannot update - tree layout not initialized');
|
|
795
|
+
// Try to create tree layout
|
|
796
|
+
if (typeof d3 !== 'undefined') {
|
|
797
|
+
this.treeLayout = d3.tree().size([this.height, this.width]);
|
|
798
|
+
console.log('Created tree layout in update()');
|
|
799
|
+
} else {
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// Ensure source has valid data
|
|
805
|
+
if (!source || !source.data) {
|
|
806
|
+
console.error('ActivityTree: Invalid source in update()', source);
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// Ensure we have a valid root
|
|
811
|
+
if (!this.root) {
|
|
812
|
+
console.error('ActivityTree: No root node available for update');
|
|
676
813
|
return;
|
|
677
814
|
}
|
|
678
815
|
|
|
679
816
|
// Compute the new tree layout
|
|
680
|
-
|
|
817
|
+
let treeData;
|
|
818
|
+
try {
|
|
819
|
+
treeData = this.treeLayout(this.root);
|
|
820
|
+
} catch (error) {
|
|
821
|
+
console.error('ActivityTree: Error computing tree layout:', error);
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
|
|
681
825
|
const nodes = treeData.descendants();
|
|
682
826
|
const links = treeData.links();
|
|
683
827
|
|
|
684
828
|
console.log(`ActivityTree: Updating tree with ${nodes.length} nodes`);
|
|
829
|
+
|
|
830
|
+
// Check if we actually have the tree container
|
|
831
|
+
if (nodes.length === 1 && this.container) {
|
|
832
|
+
// Only root node exists, ensure container shows the tree
|
|
833
|
+
const svgElement = this.container.querySelector('svg');
|
|
834
|
+
if (!svgElement) {
|
|
835
|
+
console.warn('SVG element not found in container after update');
|
|
836
|
+
}
|
|
837
|
+
}
|
|
685
838
|
|
|
686
839
|
// Normalize for fixed-depth
|
|
687
840
|
nodes.forEach((d) => {
|
|
@@ -969,13 +1122,33 @@ class ActivityTree {
|
|
|
969
1122
|
* Update statistics
|
|
970
1123
|
*/
|
|
971
1124
|
updateStats() {
|
|
1125
|
+
// Check if we have a valid root node
|
|
1126
|
+
if (!this.root || !this.root.data) {
|
|
1127
|
+
console.warn('ActivityTree: Cannot update stats - root not initialized');
|
|
1128
|
+
// Set default values
|
|
1129
|
+
const nodeCountEl = document.getElementById('node-count');
|
|
1130
|
+
const activeCountEl = document.getElementById('active-count');
|
|
1131
|
+
const depthEl = document.getElementById('tree-depth');
|
|
1132
|
+
|
|
1133
|
+
if (nodeCountEl) nodeCountEl.textContent = '1';
|
|
1134
|
+
if (activeCountEl) activeCountEl.textContent = '0';
|
|
1135
|
+
if (depthEl) depthEl.textContent = '0';
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
972
1139
|
const nodeCount = this.countNodes(this.root);
|
|
973
1140
|
const activeCount = this.countActiveNodes(this.root.data);
|
|
974
1141
|
const depth = this.getTreeDepth(this.root);
|
|
975
1142
|
|
|
976
|
-
document.getElementById('node-count')
|
|
977
|
-
document.getElementById('active-count')
|
|
978
|
-
document.getElementById('tree-depth')
|
|
1143
|
+
const nodeCountEl = document.getElementById('node-count');
|
|
1144
|
+
const activeCountEl = document.getElementById('active-count');
|
|
1145
|
+
const depthEl = document.getElementById('tree-depth');
|
|
1146
|
+
|
|
1147
|
+
if (nodeCountEl) nodeCountEl.textContent = nodeCount;
|
|
1148
|
+
if (activeCountEl) activeCountEl.textContent = activeCount;
|
|
1149
|
+
if (depthEl) depthEl.textContent = depth;
|
|
1150
|
+
|
|
1151
|
+
console.log(`ActivityTree: Stats updated - Nodes: ${nodeCount}, Active: ${activeCount}, Depth: ${depth}`);
|
|
979
1152
|
}
|
|
980
1153
|
|
|
981
1154
|
/**
|
|
@@ -1081,6 +1254,14 @@ const setupActivityTreeListeners = () => {
|
|
|
1081
1254
|
// Store instance globally for dashboard access
|
|
1082
1255
|
window.activityTreeInstance = activityTree;
|
|
1083
1256
|
}
|
|
1257
|
+
|
|
1258
|
+
// Ensure the container is ready and clear any text
|
|
1259
|
+
const container = document.getElementById('activity-tree-container') || document.getElementById('activity-tree');
|
|
1260
|
+
if (container && container.textContent && container.textContent.trim()) {
|
|
1261
|
+
console.log('Clearing text from activity tree container before init:', container.textContent);
|
|
1262
|
+
container.textContent = '';
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1084
1265
|
// Always try to initialize when tab becomes active, even if instance exists
|
|
1085
1266
|
// Small delay to ensure DOM is ready and tab is visible
|
|
1086
1267
|
setTimeout(() => {
|
|
@@ -1097,9 +1278,13 @@ const setupActivityTreeListeners = () => {
|
|
|
1097
1278
|
if (tabName === 'activity') {
|
|
1098
1279
|
console.log('Activity tab button clicked, initializing tree...');
|
|
1099
1280
|
initializeActivityTree();
|
|
1100
|
-
// Also call renderWhenVisible to ensure proper rendering
|
|
1281
|
+
// Also call renderWhenVisible and forceShow to ensure proper rendering
|
|
1101
1282
|
if (activityTree) {
|
|
1102
|
-
setTimeout(() =>
|
|
1283
|
+
setTimeout(() => {
|
|
1284
|
+
activityTree.renderWhenVisible();
|
|
1285
|
+
// Force show to ensure SVG is visible
|
|
1286
|
+
activityTree.forceShow();
|
|
1287
|
+
}, 150);
|
|
1103
1288
|
}
|
|
1104
1289
|
}
|
|
1105
1290
|
});
|
|
@@ -1110,9 +1295,13 @@ const setupActivityTreeListeners = () => {
|
|
|
1110
1295
|
if (e.detail && e.detail.newTab === 'activity') {
|
|
1111
1296
|
console.log('Tab changed to activity, initializing tree...');
|
|
1112
1297
|
initializeActivityTree();
|
|
1113
|
-
// Also call renderWhenVisible to ensure proper rendering
|
|
1298
|
+
// Also call renderWhenVisible and forceShow to ensure proper rendering
|
|
1114
1299
|
if (activityTree) {
|
|
1115
|
-
setTimeout(() =>
|
|
1300
|
+
setTimeout(() => {
|
|
1301
|
+
activityTree.renderWhenVisible();
|
|
1302
|
+
// Force show to ensure SVG is visible
|
|
1303
|
+
activityTree.forceShow();
|
|
1304
|
+
}, 150);
|
|
1116
1305
|
}
|
|
1117
1306
|
}
|
|
1118
1307
|
});
|
|
@@ -1120,8 +1309,18 @@ const setupActivityTreeListeners = () => {
|
|
|
1120
1309
|
// Check if activity tab is already active on load
|
|
1121
1310
|
const activeTab = document.querySelector('.tab-button.active');
|
|
1122
1311
|
if (activeTab && activeTab.getAttribute('data-tab') === 'activity') {
|
|
1312
|
+
console.log('Activity tab is active on load, initializing tree...');
|
|
1123
1313
|
initializeActivityTree();
|
|
1124
1314
|
}
|
|
1315
|
+
|
|
1316
|
+
// Also check the tab panel directly
|
|
1317
|
+
const activityPanel = document.getElementById('activity-tab');
|
|
1318
|
+
if (activityPanel && activityPanel.classList.contains('active')) {
|
|
1319
|
+
console.log('Activity panel is active on load, initializing tree...');
|
|
1320
|
+
if (!activityTree) {
|
|
1321
|
+
initializeActivityTree();
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1125
1324
|
|
|
1126
1325
|
// Export for debugging
|
|
1127
1326
|
window.activityTree = () => activityTree; // Expose instance getter for debugging
|
|
@@ -36,8 +36,6 @@ export class BuildTracker {
|
|
|
36
36
|
* Initialize the build tracker component
|
|
37
37
|
*/
|
|
38
38
|
async init() {
|
|
39
|
-
console.log('Initializing BuildTracker component');
|
|
40
|
-
|
|
41
39
|
// Try to load version.json for dashboard version
|
|
42
40
|
await this.loadDashboardVersion();
|
|
43
41
|
|
|
@@ -67,11 +65,10 @@ export class BuildTracker {
|
|
|
67
65
|
full_version: versionData.full_version || "v1.0.0-0001"
|
|
68
66
|
};
|
|
69
67
|
|
|
70
|
-
|
|
68
|
+
// Dashboard version loaded successfully
|
|
71
69
|
}
|
|
72
70
|
} catch (error) {
|
|
73
71
|
// Silently fall back to defaults if version.json not available
|
|
74
|
-
console.debug('Dashboard version.json not available, using defaults');
|
|
75
72
|
}
|
|
76
73
|
}
|
|
77
74
|
|
|
@@ -162,8 +159,6 @@ export class BuildTracker {
|
|
|
162
159
|
* @param {Object} buildInfo - Build information from server
|
|
163
160
|
*/
|
|
164
161
|
updateBuildInfo(buildInfo) {
|
|
165
|
-
console.log('Updating build info:', buildInfo);
|
|
166
|
-
|
|
167
162
|
// Store the build info
|
|
168
163
|
this.buildInfo = buildInfo;
|
|
169
164
|
|
|
@@ -252,8 +247,7 @@ export class BuildTracker {
|
|
|
252
247
|
}
|
|
253
248
|
}
|
|
254
249
|
|
|
255
|
-
//
|
|
256
|
-
console.log('Version Information:\n' + info.join('\n'));
|
|
250
|
+
// Version information compiled
|
|
257
251
|
|
|
258
252
|
// Create a simple modal-like display
|
|
259
253
|
const modal = document.createElement('div');
|
|
@@ -285,12 +279,20 @@ export class BuildTracker {
|
|
|
285
279
|
? document.querySelector(parent)
|
|
286
280
|
: parent;
|
|
287
281
|
|
|
288
|
-
if (
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
282
|
+
if (!this.element) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (!parentElement) {
|
|
287
|
+
return;
|
|
293
288
|
}
|
|
289
|
+
|
|
290
|
+
// Check if already mounted to prevent duplicates
|
|
291
|
+
if (this.element.parentNode === parentElement) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
parentElement.appendChild(this.element);
|
|
294
296
|
}
|
|
295
297
|
|
|
296
298
|
/**
|