From fe2120fd8d2e0cfbf77a908dd64c637df1bc0f4f Mon Sep 17 00:00:00 2001 From: luccast <2213102+luccast@users.noreply.github.com> Date: Sun, 25 Jan 2026 18:21:07 -0500 Subject: [PATCH] Add CrabNode to ActionGraph for enhanced session visualization - Introduced CrabNode to represent a central point in the ActionGraph. - Updated nodeTypes to include CrabNode and modified rawNodes to always include it. - Established connections from CrabNode to visible session nodes for better clarity. - Adjusted layout logic to ensure proper positioning of nodes, especially when only the CrabNode is present. - Updated MiniMap to visually distinguish CrabNode with a unique color. --- src/components/monitor/ActionGraph.tsx | 41 ++++++++++++++++++++++++-- src/components/monitor/CrabNode.tsx | 23 +++++++++++++++ src/components/monitor/index.ts | 1 + src/routes/monitor/index.tsx | 35 ++++------------------ 4 files changed, 69 insertions(+), 31 deletions(-) create mode 100644 src/components/monitor/CrabNode.tsx diff --git a/src/components/monitor/ActionGraph.tsx b/src/components/monitor/ActionGraph.tsx index 142deea..aa86aca 100644 --- a/src/components/monitor/ActionGraph.tsx +++ b/src/components/monitor/ActionGraph.tsx @@ -14,6 +14,7 @@ import { import '@xyflow/react/dist/style.css' import { SessionNode } from './SessionNode' import { ActionNode } from './ActionNode' +import { CrabNode } from './CrabNode' import { layoutGraph } from '~/lib/graph-layout' import type { MonitorSession, MonitorAction } from '~/integrations/clawdbot' @@ -24,10 +25,13 @@ interface ActionGraphProps { onSessionSelect: (key: string | null) => void } +const CRAB_NODE_ID = 'crab-origin' + // eslint-disable-next-line @typescript-eslint/no-explicit-any const nodeTypes: NodeTypes = { session: SessionNode as any, action: ActionNode as any, + crab: CrabNode as any, } export function ActionGraph({ @@ -46,6 +50,15 @@ export function ActionGraph({ const rawNodes = useMemo(() => { const nodes: Node[] = [] + // Always add the central crab node + const hasActivity = sessions.length > 0 || visibleActions.length > 0 + nodes.push({ + id: CRAB_NODE_ID, + type: 'crab', + position: { x: 0, y: 0 }, + data: { active: hasActivity }, + }) + // Add session nodes const visibleSessions = selectedSession ? sessions.filter((s) => s.key === selectedSession) @@ -77,6 +90,22 @@ export function ActionGraph({ const rawEdges = useMemo(() => { const edges: Edge[] = [] + // Get visible sessions for edge creation + const visibleSessions = selectedSession + ? sessions.filter((s) => s.key === selectedSession) + : sessions + + // Connect crab to each session + for (const session of visibleSessions) { + edges.push({ + id: `e-crab-${session.key}`, + source: CRAB_NODE_ID, + target: `session-${session.key}`, + markerEnd: { type: MarkerType.ArrowClosed }, + style: { stroke: '#06b6d4', strokeWidth: 2 }, + }) + } + // Group actions by runId const runActions = new Map() for (const action of visibleActions) { @@ -117,11 +146,18 @@ export function ActionGraph({ } return edges - }, [visibleActions]) + }, [sessions, visibleActions, selectedSession]) // Apply layout const { nodes: layoutedNodes, edges: layoutedEdges } = useMemo(() => { - if (rawNodes.length === 0) return { nodes: [], edges: [] } + // Always have at least the crab node + if (rawNodes.length === 1) { + // Just the crab node, center it + return { + nodes: [{ ...rawNodes[0]!, position: { x: 0, y: 0 } }], + edges: [], + } + } return layoutGraph(rawNodes, rawEdges, { direction: 'TB', nodeWidth: 200, @@ -170,6 +206,7 @@ export function ActionGraph({ { + if (node.type === 'crab') return '#f97316' // orange for crab if (node.type === 'session') return '#06b6d4' return '#6b7280' }} diff --git a/src/components/monitor/CrabNode.tsx b/src/components/monitor/CrabNode.tsx new file mode 100644 index 0000000..0a5235e --- /dev/null +++ b/src/components/monitor/CrabNode.tsx @@ -0,0 +1,23 @@ +import { Handle, Position } from '@xyflow/react' +import { motion } from 'framer-motion' + +interface CrabNodeProps { + data: { active: boolean } +} + +export function CrabNode({ data }: CrabNodeProps) { + return ( + +
🦀
+ +
+ ) +} diff --git a/src/components/monitor/index.ts b/src/components/monitor/index.ts index 815e0a0..3341223 100644 --- a/src/components/monitor/index.ts +++ b/src/components/monitor/index.ts @@ -2,5 +2,6 @@ export { ActionGraph } from './ActionGraph' export { SessionList } from './SessionList' export { SessionNode } from './SessionNode' export { ActionNode } from './ActionNode' +export { CrabNode } from './CrabNode' export { StatusIndicator } from './StatusIndicator' export { SettingsPanel } from './SettingsPanel' diff --git a/src/routes/monitor/index.tsx b/src/routes/monitor/index.tsx index ba17f2a..5c59264 100644 --- a/src/routes/monitor/index.tsx +++ b/src/routes/monitor/index.tsx @@ -189,35 +189,12 @@ function MonitorPage() { {/* Graph area */}
- {sessions.length === 0 && !connecting ? ( -
-
- -

No active sessions

-

- {connected - ? 'Waiting for agent activity...' - : 'Connect to clawdbot gateway to start monitoring'} -

- {!connected && ( - - )} -
-
- ) : ( - - )} +