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:
- Open it in the canvas
- Scroll around to see all nodes
- Mentally categorize what's there
- Check the Component Inputs/Outputs nodes to understand the interface
- Hunt for REST/Function calls scattered around
- 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
-
As a developer reviewing code, I want to quickly understand what a component does without diving into the canvas.
-
As a developer debugging, I want to see all external dependencies (API calls, events) in one place.
-
As a developer refactoring, I want to know everywhere this component is used before I change it.
-
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)
- Implement
buildComponentXRay()function - Extract inputs/outputs from Component Inputs/Outputs nodes
- Detect subcomponent usages
- Find REST, Event, Function nodes
- 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)
- Create
ComponentXRayViewReact component - Implement collapsible sections
- Style the card layout
- Add icons for categories
Verification:
- All sections render correctly
- Sections expand/collapse
- Looks clean and readable
Phase 3: Interactivity (0.5-1 day)
- Implement "Open in Canvas" navigation
- Implement "Find Node" navigation
- Implement "Show X-Ray" for subcomponents
- Add "Find receivers/senders" for events
- Wire up to Analysis Panel context
Verification:
- All navigation links work
- Can drill into subcomponents
- Event tracking works
Phase 4: Polish (0.5 day)
- Add collapsed summary view
- Improve typography and spacing
- Add empty state handling
- 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)
Known Issues
AI Function Node Sidebar Disappearing Bug
Status: Open (Not Fixed)
Severity: Medium
Date Discovered: January 2026
Description: When clicking on AI-generated function nodes in the Component X-Ray panel's "Functions" section, the left sidebar navigation toolbar disappears from view.
Technical Details:
- The
.ToolbarCSS class inSideNavigation.module.scssloses itsflex-direction: columnproperty - This appears to be related to the
AiPropertyEditorcomponent which usesTabsVariant.Sidebartabs - The AiPropertyEditor renders for AI-generated nodes and displays tabs for "AI Chat" and "Properties"
- Investigation showed the TabsVariant.Sidebar CSS doesn't directly manipulate parent elements
- Attempted fix with CSS
!importantrules on the Toolbar did not resolve the issue
Impact:
- Users cannot access the main left sidebar navigation after clicking AI function nodes from X-Ray panel
- Workaround: Close the property editor or switch to a different panel to restore the toolbar
Root Cause: Unknown - the exact mechanism causing the CSS property to disappear has not been identified. The issue likely involves complex CSS cascade interactions between:
- SideNavigation component styles
- AiPropertyEditor component styles
- TabsVariant.Sidebar tab system styles
Investigation Files:
packages/noodl-core-ui/src/components/app/SideNavigation/SideNavigation.module.scsspackages/noodl-editor/src/editor/src/views/panels/propertyeditor/index.tsx(AiPropertyEditor)packages/noodl-editor/src/editor/src/models/sidebar/sidebarmodel.tsx(switchToNode method)
Next Steps: Future investigation should focus on:
- Using React DevTools to inspect component tree when bug occurs
- Checking if TabsVariant.Sidebar modifies parent DOM structure
- Looking for JavaScript that directly manipulates Toolbar styles
- Testing if the issue reproduces with other sidebar panels open