mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-12 07:12:54 +01:00
Finished component sidebar updates, with one small bug remaining and documented
This commit is contained in:
@@ -0,0 +1,833 @@
|
||||
# VIEW-000: Foundation & Shared Utilities
|
||||
|
||||
## Overview
|
||||
|
||||
Build the shared infrastructure that all visualization views will depend on: graph traversal utilities, cross-component resolution, the view panel framework, and navigation helpers.
|
||||
|
||||
**Estimate:** 4-5 days
|
||||
**Priority:** CRITICAL (blocks all other VIEW tasks)
|
||||
**Complexity:** Medium
|
||||
|
||||
---
|
||||
|
||||
## Goals
|
||||
|
||||
1. Create graph analysis utilities for traversing nodes and connections
|
||||
2. Build cross-component connection resolution (follow Component Inputs/Outputs)
|
||||
3. Implement the view panel framework (tabbed container for all views)
|
||||
4. Create "Jump to Canvas" navigation helper
|
||||
5. Establish patterns and types for view implementations
|
||||
|
||||
---
|
||||
|
||||
## Why Foundation First?
|
||||
|
||||
Every view needs to:
|
||||
- Traverse the node graph in some way
|
||||
- Potentially cross component boundaries
|
||||
- Display in a consistent panel/container
|
||||
- Allow navigation back to the canvas
|
||||
|
||||
Building this once, correctly, saves massive duplication and ensures consistency.
|
||||
|
||||
### 4. Real-Time Update System
|
||||
|
||||
Views must stay in sync with canvas changes and runtime state. This requires a subscription system.
|
||||
|
||||
```typescript
|
||||
// viewUpdates.ts
|
||||
|
||||
export interface ViewUpdateSource {
|
||||
// Canvas/model changes
|
||||
onNodeAdded: (callback: (node: NodeGraphNode) => void) => Unsubscribe;
|
||||
onNodeRemoved: (callback: (nodeId: string) => void) => Unsubscribe;
|
||||
onConnectionAdded: (callback: (conn: Connection) => void) => Unsubscribe;
|
||||
onConnectionRemoved: (callback: (conn: Connection) => void) => Unsubscribe;
|
||||
onNodeParameterChanged: (callback: (nodeId: string, param: string, value: unknown) => void) => Unsubscribe;
|
||||
|
||||
// Component changes
|
||||
onComponentSwitched: (callback: (component: ComponentModel) => void) => Unsubscribe;
|
||||
|
||||
// Runtime events (for views that need them)
|
||||
onRuntimeEvent: (callback: (event: RuntimeEvent) => void) => Unsubscribe;
|
||||
}
|
||||
|
||||
// Hook for views to subscribe to updates
|
||||
export function useViewUpdates(
|
||||
dependencies: ('nodes' | 'connections' | 'runtime')[],
|
||||
callback: () => void
|
||||
): void {
|
||||
useEffect(() => {
|
||||
const unsubscribes: Unsubscribe[] = [];
|
||||
|
||||
if (dependencies.includes('nodes')) {
|
||||
unsubscribes.push(viewUpdateSource.onNodeAdded(callback));
|
||||
unsubscribes.push(viewUpdateSource.onNodeRemoved(callback));
|
||||
}
|
||||
|
||||
if (dependencies.includes('connections')) {
|
||||
unsubscribes.push(viewUpdateSource.onConnectionAdded(callback));
|
||||
unsubscribes.push(viewUpdateSource.onConnectionRemoved(callback));
|
||||
}
|
||||
|
||||
if (dependencies.includes('runtime')) {
|
||||
unsubscribes.push(viewUpdateSource.onRuntimeEvent(callback));
|
||||
}
|
||||
|
||||
return () => unsubscribes.forEach(unsub => unsub());
|
||||
}, [dependencies, callback]);
|
||||
}
|
||||
```
|
||||
|
||||
#### Update Strategy Per View
|
||||
|
||||
| View | Update Triggers | Debounce? |
|
||||
|------|-----------------|-----------|
|
||||
| Topology Map | Component added/removed | Yes (500ms) |
|
||||
| Component X-Ray | Nodes/connections in current component | Yes (200ms) |
|
||||
| Trigger Chain | Runtime events (when recording) | No (real-time) |
|
||||
| Node Census | Nodes/connections changed | Yes (300ms) |
|
||||
| Data Lineage | Connections changed, runtime values | Yes (200ms) |
|
||||
| Impact Radar | Component interface changed | Yes (500ms) |
|
||||
| Semantic Layers | Nodes added/removed | Yes (100ms) |
|
||||
|
||||
### 5. Understanding Existing Debug Infrastructure (CRITICAL)
|
||||
|
||||
**Several views need to integrate with Noodl's existing runtime debugging.** Before building those views, we need to document how the current system works.
|
||||
|
||||
The existing canvas already has powerful runtime features:
|
||||
- Nodes "light up" when they fire
|
||||
- Connections animate when data flows
|
||||
- DebugInspector shows live values on hover
|
||||
- You can pin inspectors to track values over time
|
||||
|
||||
#### Key Components to Investigate
|
||||
|
||||
```typescript
|
||||
// These likely exist and need documentation:
|
||||
|
||||
// 1. DebugInspector - manages live value inspection
|
||||
// Location: packages/noodl-editor/src/editor/src/models/DebugInspector/
|
||||
DebugInspector.instance.getValueForPort(nodeId, port);
|
||||
DebugInspector.InspectorsModel; // Manages pinned inspectors
|
||||
|
||||
// 2. Node highlighting in canvas
|
||||
// Location: packages/noodl-editor/src/editor/src/views/nodegrapheditor/
|
||||
nodeGraphEditor.highlightNode(node, duration);
|
||||
nodeGraphEditor.highlightConnection(connection, duration);
|
||||
|
||||
// 3. Runtime event emission
|
||||
// Location: packages/noodl-runtime/src/
|
||||
nodeInstance.on('outputChanged', handler);
|
||||
nodeInstance.on('signalSent', handler);
|
||||
```
|
||||
|
||||
#### Documentation Task
|
||||
|
||||
Before implementing VIEW-003 (Trigger Chain) or live features in VIEW-005 (Data Lineage), add a research phase:
|
||||
|
||||
1. **Map the debug event flow**: How do runtime events get from node execution to canvas highlighting?
|
||||
2. **Document DebugInspector API**: What methods are available? How does pinning work?
|
||||
3. **Identify extension points**: Where can we tap in to record events?
|
||||
4. **Find component boundary handling**: How does debugging work across nested components?
|
||||
|
||||
This research will be invaluable for:
|
||||
- VIEW-003: Trigger Chain Debugger (needs to record all debug events)
|
||||
- VIEW-005: Data Lineage live mode (needs live value access)
|
||||
- VIEW-007: Semantic Layers (needs to preserve highlighting behavior)
|
||||
|
||||
---
|
||||
|
||||
## Technical Design
|
||||
|
||||
### 1. Graph Analysis Module
|
||||
|
||||
**Location:** `packages/noodl-editor/src/editor/src/utils/graphAnalysis/`
|
||||
|
||||
```typescript
|
||||
// index.ts - public API
|
||||
export * from './traversal';
|
||||
export * from './crossComponent';
|
||||
export * from './categorization';
|
||||
export * from './duplicateDetection';
|
||||
export * from './types';
|
||||
```
|
||||
|
||||
#### 1.1 Traversal Utilities
|
||||
|
||||
```typescript
|
||||
// traversal.ts
|
||||
|
||||
export interface ConnectionPath {
|
||||
node: NodeGraphNode;
|
||||
port: string;
|
||||
direction: 'input' | 'output';
|
||||
connection?: Connection;
|
||||
}
|
||||
|
||||
export interface TraversalResult {
|
||||
path: ConnectionPath[];
|
||||
crossedComponents: ComponentCrossing[];
|
||||
terminatedAt: 'source' | 'sink' | 'cycle' | 'component-boundary';
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace a connection chain from a starting point.
|
||||
* Follows connections upstream (to sources) or downstream (to sinks).
|
||||
*/
|
||||
export function traceConnectionChain(
|
||||
component: ComponentModel,
|
||||
startNodeId: string,
|
||||
startPort: string,
|
||||
direction: 'upstream' | 'downstream',
|
||||
options?: {
|
||||
maxDepth?: number;
|
||||
crossComponents?: boolean;
|
||||
stopAtTypes?: string[]; // Stop when hitting these node types
|
||||
}
|
||||
): TraversalResult;
|
||||
|
||||
/**
|
||||
* Get all nodes directly connected to a given node.
|
||||
*/
|
||||
export function getConnectedNodes(
|
||||
component: ComponentModel,
|
||||
nodeId: string
|
||||
): { inputs: NodeGraphNode[]; outputs: NodeGraphNode[] };
|
||||
|
||||
/**
|
||||
* Get all connections for a specific port.
|
||||
*/
|
||||
export function getPortConnections(
|
||||
component: ComponentModel,
|
||||
nodeId: string,
|
||||
portName: string,
|
||||
direction: 'input' | 'output'
|
||||
): Connection[];
|
||||
|
||||
/**
|
||||
* Build adjacency list representation of the graph.
|
||||
*/
|
||||
export function buildAdjacencyList(
|
||||
component: ComponentModel
|
||||
): Map<string, { inputs: string[]; outputs: string[] }>;
|
||||
```
|
||||
|
||||
#### 1.2 Cross-Component Resolution
|
||||
|
||||
```typescript
|
||||
// crossComponent.ts
|
||||
|
||||
export interface ComponentCrossing {
|
||||
fromComponent: ComponentModel;
|
||||
toComponent: ComponentModel;
|
||||
viaPort: string;
|
||||
direction: 'into' | 'outof';
|
||||
}
|
||||
|
||||
export interface ComponentUsage {
|
||||
component: ComponentModel; // The component being used
|
||||
usedIn: ComponentModel; // Where it's used
|
||||
instanceNodeId: string; // The node ID of the instance
|
||||
connectedPorts: {
|
||||
port: string;
|
||||
connectedTo: { nodeId: string; port: string }[];
|
||||
}[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all places where a component is instantiated across the project.
|
||||
*/
|
||||
export function findComponentUsages(
|
||||
project: ProjectModel,
|
||||
componentName: string
|
||||
): ComponentUsage[];
|
||||
|
||||
/**
|
||||
* Resolve a Component Input/Output to its external connections.
|
||||
* Given a Component Inputs node, find what feeds into it from the parent.
|
||||
*/
|
||||
export function resolveComponentBoundary(
|
||||
project: ProjectModel,
|
||||
component: ComponentModel,
|
||||
boundaryNodeId: string, // Component Inputs or Component Outputs node
|
||||
portName: string
|
||||
): ExternalConnection[];
|
||||
|
||||
/**
|
||||
* Build complete component dependency graph for the project.
|
||||
*/
|
||||
export function buildComponentDependencyGraph(
|
||||
project: ProjectModel
|
||||
): {
|
||||
nodes: ComponentModel[];
|
||||
edges: { from: string; to: string; count: number }[];
|
||||
};
|
||||
```
|
||||
|
||||
#### 1.3 Node Categorization
|
||||
|
||||
```typescript
|
||||
// categorization.ts
|
||||
|
||||
export type NodeCategory =
|
||||
| 'visual' // Groups, Text, Image, etc.
|
||||
| 'data' // Variables, Objects, Arrays
|
||||
| 'logic' // Conditions, Expressions, Switches
|
||||
| 'events' // Send Event, Receive Event, Component I/O
|
||||
| 'api' // REST, Function, Cloud Functions
|
||||
| 'navigation' // Page Router, Navigate
|
||||
| 'animation' // Transitions, States (animation-related)
|
||||
| 'utility' // Other/misc
|
||||
|
||||
export interface CategorizedNodes {
|
||||
byCategory: Map<NodeCategory, NodeGraphNode[]>;
|
||||
byType: Map<string, NodeGraphNode[]>;
|
||||
totals: { category: NodeCategory; count: number }[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Categorize all nodes in a component by semantic type.
|
||||
*/
|
||||
export function categorizeNodes(component: ComponentModel): CategorizedNodes;
|
||||
|
||||
/**
|
||||
* Get the category for a specific node type.
|
||||
*/
|
||||
export function getNodeCategory(nodeType: string): NodeCategory;
|
||||
|
||||
/**
|
||||
* Check if a node is a visual node (has visual hierarchy).
|
||||
*/
|
||||
export function isVisualNode(node: NodeGraphNode): boolean;
|
||||
|
||||
/**
|
||||
* Check if a node is a data source (Variable, Object, Array, etc.).
|
||||
*/
|
||||
export function isDataSourceNode(node: NodeGraphNode): boolean;
|
||||
```
|
||||
|
||||
#### 1.4 Duplicate Detection
|
||||
|
||||
```typescript
|
||||
// duplicateDetection.ts
|
||||
|
||||
export interface DuplicateGroup {
|
||||
name: string;
|
||||
type: string;
|
||||
instances: {
|
||||
node: NodeGraphNode;
|
||||
component: ComponentModel;
|
||||
connectionCount: number;
|
||||
}[];
|
||||
severity: 'info' | 'warning' | 'error';
|
||||
reason: string; // Why this might be a problem
|
||||
}
|
||||
|
||||
/**
|
||||
* Find potential duplicate nodes within a component.
|
||||
* Duplicates = same type + same/similar name.
|
||||
*/
|
||||
export function findDuplicatesInComponent(
|
||||
component: ComponentModel
|
||||
): DuplicateGroup[];
|
||||
|
||||
/**
|
||||
* Find potential duplicate nodes across the entire project.
|
||||
*/
|
||||
export function findDuplicatesInProject(
|
||||
project: ProjectModel
|
||||
): DuplicateGroup[];
|
||||
|
||||
/**
|
||||
* Analyze if duplicates might cause conflicts.
|
||||
* E.g., two Variables with same name writing to same output.
|
||||
*/
|
||||
export function analyzeDuplicateConflicts(
|
||||
duplicates: DuplicateGroup[]
|
||||
): ConflictAnalysis[];
|
||||
```
|
||||
|
||||
### 2. View Panel Framework
|
||||
|
||||
**Location:** `packages/noodl-editor/src/editor/src/views/AnalysisPanel/`
|
||||
|
||||
```typescript
|
||||
// AnalysisPanel.tsx
|
||||
|
||||
export interface AnalysisView {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: IconName;
|
||||
component: React.ComponentType<AnalysisViewProps>;
|
||||
}
|
||||
|
||||
export interface AnalysisViewProps {
|
||||
project: ProjectModel;
|
||||
currentComponent: ComponentModel | null;
|
||||
selectedNodes: NodeGraphNode[];
|
||||
onNavigateToNode: (componentName: string, nodeId: string) => void;
|
||||
onNavigateToComponent: (componentName: string) => void;
|
||||
}
|
||||
|
||||
export function AnalysisPanel({
|
||||
views,
|
||||
activeViewId,
|
||||
onViewChange
|
||||
}: AnalysisPanelProps): JSX.Element;
|
||||
```
|
||||
|
||||
#### Panel Structure
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Analysis [×] │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ [🗺️] [📋] [🔍] [📊] [🔗] [💥] [📑] │
|
||||
│ Map Census Find Lin Imp Layers │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ {Active View Component} │
|
||||
│ │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ 📍 Current: Presales Page [↗ Canvas] │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2. View Infrastructure
|
||||
|
||||
**Location:** `packages/noodl-editor/src/editor/src/views/`
|
||||
|
||||
The visualization system has three types of components:
|
||||
|
||||
#### 2a. Meta View Tabs
|
||||
|
||||
These replace the canvas entirely (like Topology Map, Trigger Chain):
|
||||
|
||||
```typescript
|
||||
// MetaViewTabs.tsx - Tab bar for switching between Canvas and meta views
|
||||
|
||||
export type MetaViewId = 'canvas' | 'topology' | 'triggers';
|
||||
|
||||
export interface MetaViewDefinition {
|
||||
id: MetaViewId;
|
||||
name: string;
|
||||
icon: IconName;
|
||||
shortcut?: string;
|
||||
component: React.ComponentType<MetaViewProps>;
|
||||
}
|
||||
|
||||
export interface MetaViewProps {
|
||||
project: ProjectModel;
|
||||
onNavigateToCanvas: (componentName: string, nodeId?: string) => void;
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ ◀ ▶ │ [🗺️ Canvas] [📊 Topology] [⚡ Triggers] │ ▶ Preview │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 2b. Sidebar Panels
|
||||
|
||||
These open alongside the canvas (using existing SidebarModel):
|
||||
|
||||
```typescript
|
||||
// Already supported by SidebarModel!
|
||||
// Just register new React components:
|
||||
|
||||
SidebarModel.instance.register({
|
||||
id: 'xray',
|
||||
name: 'X-Ray',
|
||||
icon: IconName.Search,
|
||||
panel: ComponentXRayPanel // React component
|
||||
});
|
||||
|
||||
SidebarModel.instance.register({
|
||||
id: 'census',
|
||||
name: 'Census',
|
||||
icon: IconName.List,
|
||||
panel: NodeCensusPanel // React component
|
||||
});
|
||||
```
|
||||
|
||||
#### 2c. Canvas Overlays (THE BIG ONE)
|
||||
|
||||
These enhance the existing canvas with toggleable overlays:
|
||||
|
||||
```typescript
|
||||
// CanvasOverlayManager.tsx
|
||||
|
||||
export type OverlayId = 'layers' | 'lineage' | 'impact';
|
||||
|
||||
export interface CanvasOverlay {
|
||||
id: OverlayId;
|
||||
name: string;
|
||||
icon: IconName;
|
||||
|
||||
// The React component that renders over the canvas
|
||||
component: React.ComponentType<CanvasOverlayProps>;
|
||||
|
||||
// Optional panel component for controls/details
|
||||
panelComponent?: React.ComponentType<OverlayPanelProps>;
|
||||
}
|
||||
|
||||
export interface CanvasOverlayProps {
|
||||
// Canvas transform info (for positioning overlays)
|
||||
scale: number;
|
||||
pan: { x: number; y: number };
|
||||
|
||||
// Current component context
|
||||
component: ComponentModel;
|
||||
|
||||
// Callback to control canvas highlighting
|
||||
highlightAPI: CanvasHighlightAPI;
|
||||
}
|
||||
|
||||
export interface CanvasOverlayManagerProps {
|
||||
activeOverlays: Set<OverlayId>;
|
||||
onToggleOverlay: (id: OverlayId) => void;
|
||||
}
|
||||
|
||||
// Toolbar for toggling overlays
|
||||
export function CanvasOverlayToolbar({
|
||||
activeOverlays,
|
||||
onToggleOverlay
|
||||
}: CanvasOverlayManagerProps): JSX.Element;
|
||||
```
|
||||
|
||||
**Based on CommentLayer pattern** - this already exists and works!
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Login Page [Layers] [Lineage ✓] [Impact]│
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Canvas with overlays rendered on top... │
|
||||
│ Lineage highlights glowing on connections... │
|
||||
│ │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ 🔗 Lineage Panel (when active) [×] │
|
||||
│ Details about the current lineage trace... │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3. Canvas Highlighting API (Critical for Overlays)
|
||||
|
||||
**This is what makes the overlays legendary!**
|
||||
|
||||
The canvas needs an API for persistent, multi-channel highlighting:
|
||||
|
||||
```typescript
|
||||
// canvasHighlight.ts
|
||||
|
||||
export interface CanvasHighlightAPI {
|
||||
// Create a new highlight (returns handle to control it)
|
||||
highlightNodes(nodeIds: string[], options: HighlightOptions): HighlightHandle;
|
||||
highlightConnections(connections: ConnectionRef[], options: HighlightOptions): HighlightHandle;
|
||||
highlightPath(path: PathDefinition, options: HighlightOptions): HighlightHandle;
|
||||
|
||||
// Query active highlights
|
||||
getActiveHighlights(): HighlightInfo[];
|
||||
|
||||
// Clear all highlights in a channel
|
||||
clearChannel(channel: string): void;
|
||||
|
||||
// Clear everything
|
||||
clearAll(): void;
|
||||
}
|
||||
|
||||
export interface HighlightOptions {
|
||||
channel: string; // 'lineage', 'impact', 'selection', etc.
|
||||
color?: string; // Override default channel color
|
||||
style?: 'solid' | 'pulse' | 'glow';
|
||||
persistent?: boolean; // Stay until explicitly dismissed (default: true)
|
||||
label?: string; // Optional label shown near highlight
|
||||
}
|
||||
|
||||
export interface HighlightHandle {
|
||||
id: string;
|
||||
channel: string;
|
||||
update(nodeIds: string[]): void; // Change what's highlighted
|
||||
dismiss(): void; // Remove this highlight
|
||||
}
|
||||
|
||||
export interface PathDefinition {
|
||||
nodes: string[]; // Ordered list of node IDs in the path
|
||||
connections: ConnectionRef[]; // Connections between them
|
||||
crossesComponents?: boolean; // True if path spans multiple components
|
||||
}
|
||||
|
||||
// Channel defaults
|
||||
const HIGHLIGHT_CHANNELS = {
|
||||
lineage: { color: '#4A90D9', style: 'glow' }, // Blue glow for data flow
|
||||
impact: { color: '#F5A623', style: 'pulse' }, // Orange pulse for dependencies
|
||||
selection: { color: '#FFFFFF', style: 'solid' }, // White for selection
|
||||
warning: { color: '#FF6B6B', style: 'pulse' } // Red pulse for issues
|
||||
};
|
||||
```
|
||||
|
||||
#### Key Behavior: Persistent Across Navigation!
|
||||
|
||||
When you navigate from Presales Page to its parent App Shell, the lineage highlights should:
|
||||
1. Stay visible on any nodes that exist in the new view
|
||||
2. Show an indicator: "⬆️ Path continues in child: Presales Page [Go ↗]"
|
||||
|
||||
```typescript
|
||||
// Highlight state is stored globally, not per-component
|
||||
interface GlobalHighlightState {
|
||||
activeHighlights: Map<string, {
|
||||
handle: HighlightHandle;
|
||||
affectedComponents: string[]; // Which components have nodes in this highlight
|
||||
currentlyVisible: string[]; // Node IDs visible in current component
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
#### Navigation Helpers
|
||||
|
||||
```typescript
|
||||
// navigation.ts
|
||||
|
||||
/**
|
||||
* Navigate to a specific node on the canvas.
|
||||
* - Switches to the correct component if needed
|
||||
* - Pans the canvas to center on the node
|
||||
* - Selects the node
|
||||
* - Does NOT dismiss active highlights (they persist!)
|
||||
*/
|
||||
export function navigateToNode(
|
||||
componentName: string,
|
||||
nodeId: string,
|
||||
options?: {
|
||||
select?: boolean;
|
||||
highlight?: boolean; // Add temporary highlight on top of persistent ones
|
||||
zoomToFit?: boolean;
|
||||
}
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Navigate to a component (open it in the canvas).
|
||||
* Active highlights update to show relevant portions.
|
||||
*/
|
||||
export function navigateToComponent(
|
||||
componentName: string
|
||||
): void;
|
||||
```
|
||||
|
||||
### 4. Shared Types
|
||||
|
||||
```typescript
|
||||
// types.ts
|
||||
|
||||
export interface NodeSummary {
|
||||
id: string;
|
||||
type: string;
|
||||
displayName: string;
|
||||
label: string | null; // User-assigned label
|
||||
category: NodeCategory;
|
||||
inputCount: number;
|
||||
outputCount: number;
|
||||
connectedInputs: number;
|
||||
connectedOutputs: number;
|
||||
hasChildren: boolean; // For visual nodes
|
||||
childCount: number;
|
||||
}
|
||||
|
||||
export interface ConnectionSummary {
|
||||
fromNode: NodeSummary;
|
||||
fromPort: string;
|
||||
toNode: NodeSummary;
|
||||
toPort: string;
|
||||
}
|
||||
|
||||
export interface ComponentSummary {
|
||||
name: string;
|
||||
fullName: string;
|
||||
nodeCount: number;
|
||||
connectionCount: number;
|
||||
inputPorts: string[];
|
||||
outputPorts: string[];
|
||||
usedComponents: string[]; // Subcomponents used
|
||||
usedByComponents: string[]; // Components that use this one
|
||||
categories: { category: NodeCategory; count: number }[];
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1: Core Traversal (1 day)
|
||||
|
||||
1. Create `graphAnalysis/` folder structure
|
||||
2. Implement `traversal.ts`:
|
||||
- `traceConnectionChain()`
|
||||
- `getConnectedNodes()`
|
||||
- `getPortConnections()`
|
||||
- `buildAdjacencyList()`
|
||||
3. Write unit tests for traversal functions
|
||||
4. Test with real component data
|
||||
|
||||
**Verification:**
|
||||
- [ ] Can trace a connection chain forward and backward
|
||||
- [ ] Correctly handles branching (one output to multiple inputs)
|
||||
- [ ] Stops at specified depth limits
|
||||
- [ ] Handles cycles without infinite loops
|
||||
|
||||
### Phase 2: Cross-Component Resolution (1 day)
|
||||
|
||||
1. Implement `crossComponent.ts`:
|
||||
- `findComponentUsages()`
|
||||
- `resolveComponentBoundary()`
|
||||
- `buildComponentDependencyGraph()`
|
||||
2. Handle Component Inputs/Outputs nodes specially
|
||||
3. Test with nested component scenarios
|
||||
|
||||
**Verification:**
|
||||
- [ ] Can find all places a component is used
|
||||
- [ ] Can resolve what feeds into a Component Input from the parent
|
||||
- [ ] Dependency graph correctly shows component relationships
|
||||
|
||||
### Phase 3: Categorization & Duplicate Detection (0.5 days)
|
||||
|
||||
1. Implement `categorization.ts`
|
||||
2. Implement `duplicateDetection.ts`
|
||||
3. Create category mapping for all known node types
|
||||
|
||||
**Verification:**
|
||||
- [ ] All node types correctly categorized
|
||||
- [ ] Duplicates detected based on name + type
|
||||
- [ ] Severity levels assigned appropriately
|
||||
|
||||
### Phase 4: View Switcher Framework (1 day)
|
||||
|
||||
1. Create `ViewSwitcher/` component structure
|
||||
2. Implement dropdown UI with view list
|
||||
3. Wire up view switching (replace canvas area content)
|
||||
4. Implement state persistence (localStorage)
|
||||
5. Add keyboard shortcuts for view switching
|
||||
6. Integrate with existing canvas header
|
||||
|
||||
**Verification:**
|
||||
- [ ] Dropdown appears in canvas header
|
||||
- [ ] Clicking view switches content area
|
||||
- [ ] State persists across page reloads
|
||||
- [ ] Keyboard shortcuts work
|
||||
- [ ] "Node Canvas" view shows existing canvas (no regression)
|
||||
|
||||
### Phase 5: Navigation Helpers (0.5 days)
|
||||
|
||||
1. Implement `navigateToCanvas()` with all options
|
||||
2. Implement `highlightNodes()` / `highlightConnectionPath()`
|
||||
3. Integrate with NodeGraphEditor
|
||||
4. Test jumping from placeholder views
|
||||
|
||||
**Verification:**
|
||||
- [ ] Clicking a node reference in any view jumps to canvas
|
||||
- [ ] Node is centered and selected
|
||||
- [ ] Temporary highlight works for tracing
|
||||
- [ ] Works across component boundaries
|
||||
|
||||
### Phase 6: Debug Infrastructure Documentation (0.5 days)
|
||||
|
||||
1. Research and document existing DebugInspector system
|
||||
2. Document node highlighting mechanism
|
||||
3. Document runtime event emission
|
||||
4. Identify extension points for VIEW-003 and VIEW-005
|
||||
5. Create `DEBUG-INFRASTRUCTURE.md` reference doc
|
||||
|
||||
**Verification:**
|
||||
- [ ] DebugInspector API documented
|
||||
- [ ] Node highlighting mechanism understood
|
||||
- [ ] Clear path for Trigger Chain integration
|
||||
|
||||
---
|
||||
|
||||
## Files to Create
|
||||
|
||||
```
|
||||
packages/noodl-editor/src/editor/src/
|
||||
├── utils/
|
||||
│ └── graphAnalysis/
|
||||
│ ├── index.ts
|
||||
│ ├── traversal.ts
|
||||
│ ├── crossComponent.ts
|
||||
│ ├── categorization.ts
|
||||
│ ├── duplicateDetection.ts
|
||||
│ ├── navigation.ts
|
||||
│ └── types.ts
|
||||
├── views/
|
||||
│ ├── ViewSwitcher/
|
||||
│ │ ├── index.ts
|
||||
│ │ ├── ViewSwitcher.tsx
|
||||
│ │ ├── ViewSwitcher.module.scss
|
||||
│ │ ├── ViewDropdown.tsx
|
||||
│ │ ├── viewDefinitions.ts
|
||||
│ │ └── viewStateStore.ts
|
||||
│ └── AnalysisViews/
|
||||
│ ├── index.ts
|
||||
│ ├── shared/
|
||||
│ │ ├── ViewContainer.tsx # Common wrapper for all views
|
||||
│ │ ├── NodeReference.tsx # Clickable node link component
|
||||
│ │ └── ComponentBadge.tsx # Component name badge with navigation
|
||||
│ └── [individual view folders created by VIEW-001 through VIEW-007]
|
||||
└── docs/
|
||||
└── DEBUG-INFRASTRUCTURE.md # Documentation of existing debug system
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
|
||||
```typescript
|
||||
// __tests__/traversal.test.ts
|
||||
|
||||
describe('traceConnectionChain', () => {
|
||||
it('follows a simple linear chain', () => { ... });
|
||||
it('handles branching outputs', () => { ... });
|
||||
it('stops at max depth', () => { ... });
|
||||
it('detects and handles cycles', () => { ... });
|
||||
it('crosses component boundaries when enabled', () => { ... });
|
||||
});
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
|
||||
- Load a real complex project
|
||||
- Run traversal from various starting points
|
||||
- Verify results match manual inspection
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] All traversal functions work correctly on complex graphs
|
||||
- [ ] Cross-component resolution handles nested components
|
||||
- [ ] View panel integrates cleanly with existing sidebar
|
||||
- [ ] Navigation to canvas works from external code
|
||||
- [ ] Types are comprehensive and well-documented
|
||||
- [ ] Unit tests cover edge cases
|
||||
|
||||
---
|
||||
|
||||
## Risks & Mitigations
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| Performance on large projects | Add pagination/lazy loading, cache results |
|
||||
| Missing node type categorizations | Start with common types, add others as discovered |
|
||||
| Complex component nesting | Test with deeply nested scenarios early |
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
- None (this is the foundation)
|
||||
|
||||
## Blocks
|
||||
|
||||
- VIEW-001 through VIEW-007 (all visualization views)
|
||||
Reference in New Issue
Block a user