/** * @file Debug overlay component */ import * as React from "react"; import { useNodeEditor } from "../../contexts/composed/node-editor/context"; import { useNodeCanvas } from "../../contexts/composed/canvas/viewport/context"; import { useEditorActionState } from "../../contexts/composed/EditorActionStateContext"; import { useCanvasInteraction } from "../../contexts/composed/canvas/interaction/context"; import styles from "./DebugOverlay.module.css"; export type DebugOverlayProps = { /** Which debug information to show */ showSections?: { viewport?: boolean; nodes?: boolean; connections?: boolean; actions?: boolean; performance?: boolean; }; }; /** * Debug overlay component that shows internal state information * Useful for development and debugging */ export const DebugOverlay: React.FC = ({ showSections = { viewport: true, nodes: true, connections: true, actions: true, performance: false, }, }) => { const { state: editorState } = useNodeEditor(); const { state: canvasState } = useNodeCanvas(); const { state: actionState } = useEditorActionState(); const { state: interactionState, actions: _interactionActions } = useCanvasInteraction(); const [isCollapsed, setIsCollapsed] = React.useState(false); const [performanceStats, setPerformanceStats] = React.useState({ renderCount: 0, lastRenderTime: 0, }); // Track render performance only when performance section is enabled React.useEffect(() => { if (showSections.performance) { setPerformanceStats((prev) => ({ renderCount: prev.renderCount + 1, lastRenderTime: Date.now() - (prev.lastRenderTime || Date.now()), })); } }, [showSections.performance]); const nodeCount = Object.keys(editorState.nodes).length; const connectionCount = Object.keys(editorState.connections).length; const selectedNodeCount = actionState.selectedNodeIds.length; return (
Debug Info
{!isCollapsed && (
{showSections.viewport && (
Viewport
Offset: {Math.round(canvasState.viewport.offset.x)}, {Math.round(canvasState.viewport.offset.y)}
Scale: {canvasState.viewport.scale.toFixed(2)}x
Grid: {canvasState.gridSettings.enabled ? "On" : "Off"} {canvasState.gridSettings.enabled && ` (${canvasState.gridSettings.size}px)`}
)} {showSections.nodes && (
Nodes
Total: {nodeCount}
Selected: {selectedNodeCount}
{Object.entries( Object.values(editorState.nodes).reduce( (acc, node) => { acc[node.type] = (acc[node.type] || 0) + 1; return acc; }, {} as Record, ), ).map(([type, count]) => (
{type}: {count}
))}
)} {showSections.connections && (
Connections
Total: {connectionCount}
)} {showSections.actions && (
Actions
Dragging: {interactionState.dragState ? `${interactionState.dragState.nodeIds.length} nodes` : "None"}
Resizing: {interactionState.resizeState ? interactionState.resizeState.nodeId : "None"}
Connecting: {interactionState.connectionDragState ? "Active" : "None"}
Panning: {canvasState.panState.isPanning ? "Active" : "None"}
)} {showSections.performance && (
Performance
Renders: {performanceStats.renderCount}
Last render: {performanceStats.lastRenderTime.toFixed(2)}ms
)}
)}
); }; DebugOverlay.displayName = "DebugOverlay";