mirror of
https://github.com/crabwise-ai/crabwalk.git
synced 2026-08-14 00:57:52 +00:00
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.
This commit is contained in:
@@ -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<string, MonitorAction[]>()
|
||||
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({
|
||||
<MiniMap
|
||||
className="bg-gray-800! border-gray-700! rounded-lg!"
|
||||
nodeColor={(node) => {
|
||||
if (node.type === 'crab') return '#f97316' // orange for crab
|
||||
if (node.type === 'session') return '#06b6d4'
|
||||
return '#6b7280'
|
||||
}}
|
||||
|
||||
@@ -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 (
|
||||
<motion.div
|
||||
className="relative flex items-center justify-center"
|
||||
animate={data.active ? { scale: [1, 1.05, 1] } : {}}
|
||||
transition={{ repeat: Infinity, duration: 2 }}
|
||||
>
|
||||
<div className="text-6xl select-none">🦀</div>
|
||||
<Handle
|
||||
type="source"
|
||||
position={Position.Bottom}
|
||||
className="bg-cyan-500! w-3! h-3! border-2! border-gray-900!"
|
||||
/>
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
@@ -189,35 +189,12 @@ function MonitorPage() {
|
||||
|
||||
{/* Graph area */}
|
||||
<div className="flex-1">
|
||||
{sessions.length === 0 && !connecting ? (
|
||||
<div className="h-full flex items-center justify-center text-gray-500">
|
||||
<div className="text-center">
|
||||
<Activity size={48} className="mx-auto mb-4 opacity-50" />
|
||||
<p className="text-lg mb-2">No active sessions</p>
|
||||
<p className="text-sm">
|
||||
{connected
|
||||
? 'Waiting for agent activity...'
|
||||
: 'Connect to clawdbot gateway to start monitoring'}
|
||||
</p>
|
||||
{!connected && (
|
||||
<button
|
||||
onClick={handleConnect}
|
||||
disabled={connecting}
|
||||
className="mt-4 px-4 py-2 bg-cyan-600 hover:bg-cyan-500 rounded-lg text-sm font-medium disabled:opacity-50"
|
||||
>
|
||||
Connect
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<ActionGraph
|
||||
sessions={sessions}
|
||||
actions={actions}
|
||||
selectedSession={selectedSession}
|
||||
onSessionSelect={setSelectedSession}
|
||||
/>
|
||||
)}
|
||||
<ActionGraph
|
||||
sessions={sessions}
|
||||
actions={actions}
|
||||
selectedSession={selectedSession}
|
||||
onSessionSelect={setSelectedSession}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user