Files
OpenNoodl/dev-docs/tasks/phase-4-canvas-visualisation-views/VIEW-002-component-xray

VIEW-002: Component X-Ray

View Type: 📋 Sidebar Panel (opens alongside canvas)

Overview

A summary card view that shows everything important about a component at a glance: what it does, what goes in and out, what it contains, where it's used, and what external calls it makes. Think of it as the "component profile page."

Estimate: 2-3 days
Priority: HIGH
Complexity: Low
Dependencies: VIEW-000 (Foundation)


The Problem

To understand a component today, you have to:

  1. Open it in the canvas
  2. Scroll around to see all nodes
  3. Mentally categorize what's there
  4. Check the Component Inputs/Outputs nodes to understand the interface
  5. Hunt for REST/Function calls scattered around
  6. Go back to the Components Panel to see if it's used elsewhere

There's no quick "tell me about this component" view.


The Solution

A single-screen summary that answers:

  • What does this component do? (Node breakdown by category)
  • What's the interface? (Inputs and outputs)
  • What's inside? (Subcomponents used)
  • Where is it used? (Parent components)
  • What external things does it touch? (REST, Functions, Events)

User Stories

  1. As a developer reviewing code, I want to quickly understand what a component does without diving into the canvas.

  2. As a developer debugging, I want to see all external dependencies (API calls, events) in one place.

  3. As a developer refactoring, I want to know everywhere this component is used before I change it.

  4. As a new team member, I want to understand component interfaces (inputs/outputs) without reading the implementation.


UI Design

X-Ray Card View

┌─────────────────────────────────────────────────────────────────┐
│ Component X-Ray                                     [Open →]    │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 🧩 AuthFlow                                                 │ │
│ │ /Components/Auth/AuthFlow                                   │ │
│ └─────────────────────────────────────────────────────────────┘ │
│                                                                 │
│ ┌─ USED IN (3 places) ────────────────────────────────────────┐ │
│ │ 📄 Login Page          [→ Open]                             │ │
│ │ 📄 Settings Page       [→ Open]                             │ │
│ │ 📄 App Shell           [→ Open]                             │ │
│ └─────────────────────────────────────────────────────────────┘ │
│                                                                 │
│ ┌─ INTERFACE ─────────────────────────────────────────────────┐ │
│ │ INPUTS                      │ OUTPUTS                       │ │
│ │ ────────                    │ ─────────                     │ │
│ │ → onLoginRequest (signal)   │ currentUser (object) →        │ │
│ │ → redirectUrl (string)      │ authError (string) →          │ │
│ │ → initialMode (string)      │ isAuthenticated (boolean) →   │ │
│ │                             │ onSuccess (signal) →          │ │
│ │                             │ onFailure (signal) →          │ │
│ └─────────────────────────────────────────────────────────────┘ │
│                                                                 │
│ ┌─ CONTAINS ──────────────────────────────────────────────────┐ │
│ │ SUBCOMPONENTS                                               │ │
│ │ └─ LoginForm (component)            [→ X-Ray]               │ │
│ │ └─ SignupForm (component)           [→ X-Ray]               │ │
│ │ └─ ForgotPassword (component)       [→ X-Ray]               │ │
│ │                                                             │ │
│ │ NODE BREAKDOWN                                              │ │
│ │ ├─ 📦 Visual      12 nodes (Groups, Text, Images)          │ │
│ │ ├─ 💾 Data         5 nodes (Variables, Objects)            │ │
│ │ ├─ ⚡ Logic        8 nodes (Conditions, Expressions)       │ │
│ │ ├─ 📡 Events       3 nodes (Send/Receive Event)            │ │
│ │ └─ 🔧 Other        2 nodes                                  │ │
│ │                    ─────                                    │ │
│ │                    30 total                                 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│                                                                 │
│ ┌─ EXTERNAL DEPENDENCIES ─────────────────────────────────────┐ │
│ │ 🌐 REST Calls                                               │ │
│ │    POST /api/auth/login           [→ Find]                  │ │
│ │    POST /api/auth/signup          [→ Find]                  │ │
│ │    POST /api/auth/reset-password  [→ Find]                  │ │
│ │                                                             │ │
│ │ 📨 Events Sent                                              │ │
│ │    "auth:success"                 [→ Find receivers]        │ │
│ │    "auth:failure"                 [→ Find receivers]        │ │
│ │                                                             │ │
│ │ 📩 Events Received                                          │ │
│ │    "app:logout"                   [→ Find senders]          │ │
│ │                                                             │ │
│ │ 🔧 Functions                                                │ │
│ │    validateEmail                  [→ Find]                  │ │
│ │    hashPassword                   [→ Find]                  │ │
│ └─────────────────────────────────────────────────────────────┘ │
│                                                                 │
│ ┌─ INTERNAL STATE ────────────────────────────────────────────┐ │
│ │ Variables: authMode, errorMessage, isLoading                │ │
│ │ Objects: pendingUser, formData                              │ │
│ │ States: formState (login|signup|reset), loadingState        │ │
│ └─────────────────────────────────────────────────────────────┘ │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Collapsed View (for quick scanning)

┌─────────────────────────────────────────────────────────────────┐
│ 🧩 AuthFlow                  Used in: 3 | Nodes: 30 | APIs: 3  │
│    ↓ 3 inputs  ↑ 5 outputs   Contains: LoginForm +2            │
└─────────────────────────────────────────────────────────────────┘

Interactions

  • [Open →] - Jump to this component in the canvas
  • [→ X-Ray] - Show X-Ray for that subcomponent
  • [→ Find] - Jump to that node in the canvas
  • [→ Find receivers/senders] - Show all nodes that receive/send that event
  • Click "USED IN" item - Jump to the parent component
  • Expand/Collapse sections - Toggle visibility of sections

Technical Design

Data Model

interface ComponentXRay {
  // Identity
  name: string;
  fullName: string;
  path: string;  // Folder path
  
  // Usage
  usedIn: {
    component: ComponentModel;
    instanceCount: number;
  }[];
  
  // Interface
  inputs: {
    name: string;
    type: string;
    isSignal: boolean;
  }[];
  outputs: {
    name: string;
    type: string;
    isSignal: boolean;
  }[];
  
  // Contents
  subcomponents: {
    name: string;
    component: ComponentModel;
  }[];
  nodeBreakdown: {
    category: NodeCategory;
    count: number;
    nodeTypes: { type: string; count: number }[];
  }[];
  totalNodes: number;
  
  // External dependencies
  restCalls: {
    method: string;
    endpoint: string;
    nodeId: string;
  }[];
  eventsSent: {
    eventName: string;
    nodeId: string;
  }[];
  eventsReceived: {
    eventName: string;
    nodeId: string;
  }[];
  functionCalls: {
    functionName: string;
    nodeId: string;
  }[];
  
  // Internal state
  variables: { name: string; nodeId: string }[];
  objects: { name: string; nodeId: string }[];
  statesNodes: { 
    name: string; 
    nodeId: string; 
    states: string[];
  }[];
}

Building X-Ray Data

function buildComponentXRay(
  project: ProjectModel,
  component: ComponentModel
): ComponentXRay {
  const xray: ComponentXRay = {
    name: component.name,
    fullName: component.fullName,
    path: getComponentPath(component),
    usedIn: findComponentUsages(project, component.fullName),
    inputs: getComponentInputs(component),
    outputs: getComponentOutputs(component),
    subcomponents: [],
    nodeBreakdown: [],
    totalNodes: 0,
    restCalls: [],
    eventsSent: [],
    eventsReceived: [],
    functionCalls: [],
    variables: [],
    objects: [],
    statesNodes: []
  };
  
  // Analyze all nodes in the component
  component.graph.forEachNode((node) => {
    xray.totalNodes++;
    
    // Check for subcomponents
    if (isComponentInstance(node)) {
      xray.subcomponents.push({
        name: node.type.name,
        component: findComponent(project, node.type.name)
      });
    }
    
    // Check for REST calls
    if (node.type.name === 'REST' || node.type.name.includes('REST')) {
      xray.restCalls.push({
        method: node.parameters.method || 'GET',
        endpoint: node.parameters.endpoint || node.parameters.url,
        nodeId: node.id
      });
    }
    
    // Check for events
    if (node.type.name === 'Send Event') {
      xray.eventsSent.push({
        eventName: node.parameters.eventName || node.parameters.channel,
        nodeId: node.id
      });
    }
    if (node.type.name === 'Receive Event') {
      xray.eventsReceived.push({
        eventName: node.parameters.eventName || node.parameters.channel,
        nodeId: node.id
      });
    }
    
    // Check for functions
    if (node.type.name === 'Function' || node.type.name === 'Javascript') {
      xray.functionCalls.push({
        functionName: node.label || node.parameters.name || 'Anonymous',
        nodeId: node.id
      });
    }
    
    // Check for state nodes
    if (node.type.name === 'Variable') {
      xray.variables.push({ name: node.label || 'Unnamed', nodeId: node.id });
    }
    if (node.type.name === 'Object') {
      xray.objects.push({ name: node.label || 'Unnamed', nodeId: node.id });
    }
    if (node.type.name === 'States') {
      xray.statesNodes.push({
        name: node.label || 'Unnamed',
        nodeId: node.id,
        states: extractStatesFromNode(node)
      });
    }
  });
  
  // Build category breakdown
  xray.nodeBreakdown = buildCategoryBreakdown(component);
  
  return xray;
}

Implementation Phases

Phase 1: Data Collection (0.5-1 day)

  1. Implement buildComponentXRay() function
  2. Extract inputs/outputs from Component Inputs/Outputs nodes
  3. Detect subcomponent usages
  4. Find REST, Event, Function nodes
  5. Find state-related nodes (Variables, Objects, States)

Verification:

  • All sections populated correctly for test component
  • Subcomponent detection works
  • External dependencies found

Phase 2: Basic UI (1 day)

  1. Create ComponentXRayView React component
  2. Implement collapsible sections
  3. Style the card layout
  4. Add icons for categories

Verification:

  • All sections render correctly
  • Sections expand/collapse
  • Looks clean and readable

Phase 3: Interactivity (0.5-1 day)

  1. Implement "Open in Canvas" navigation
  2. Implement "Find Node" navigation
  3. Implement "Show X-Ray" for subcomponents
  4. Add "Find receivers/senders" for events
  5. Wire up to Analysis Panel context

Verification:

  • All navigation links work
  • Can drill into subcomponents
  • Event tracking works

Phase 4: Polish (0.5 day)

  1. Add collapsed summary view
  2. Improve typography and spacing
  3. Add empty state handling
  4. Performance optimization

Verification:

  • Collapsed view useful
  • Empty sections handled gracefully
  • Renders quickly

Files to Create

packages/noodl-editor/src/editor/src/views/AnalysisPanel/
└── ComponentXRayView/
    ├── index.ts
    ├── ComponentXRayView.tsx
    ├── ComponentXRayView.module.scss
    ├── XRaySection.tsx
    ├── InterfaceSection.tsx
    ├── ContentsSection.tsx
    ├── DependenciesSection.tsx
    ├── StateSection.tsx
    └── useComponentXRay.ts

Success Criteria

  • Shows accurate usage count
  • Shows correct inputs/outputs with types
  • Lists all subcomponents
  • Finds all REST calls with endpoints
  • Finds all Send/Receive Events
  • Finds all Function nodes
  • Node breakdown by category is accurate
  • All navigation links work
  • Renders in < 500ms

Future Enhancements

  • Diff view - Compare two components side by side
  • History - See how component changed over time (if git integrated)
  • Documentation - Allow adding/viewing component descriptions
  • Complexity score - Calculate a complexity metric
  • Warnings - Flag potential issues (unused inputs, orphan nodes)

Risks & Mitigations

Risk Mitigation
Node type detection misses edge cases Start with common types, expand based on testing
Component inputs/outputs detection fails Test with various component patterns
Too much information overwhelming Use collapsible sections, start collapsed

Dependencies

  • VIEW-000 Foundation (for traversal and categorization utilities)

Blocks

  • None (independent view)