From ced79b8d39c18b6dc6849cb10fd2b96a21fdf1c8 Mon Sep 17 00:00:00 2001 From: Anto01 Date: Tue, 20 Jan 2026 14:26:20 -0500 Subject: [PATCH] fix(canvas): Fix IntrospectionPanel to handle new NX introspection API response format - Handle expressions as object with user/internal arrays (new format) or direct array (old) - Add useMemo for expression filtering - Make extractors_available, dependent_files, warnings optional with safe access - Support both 'unit' and 'units' field names --- .../canvas/panels/IntrospectionPanel.tsx | 81 ++++++++++++++----- 1 file changed, 59 insertions(+), 22 deletions(-) diff --git a/atomizer-dashboard/frontend/src/components/canvas/panels/IntrospectionPanel.tsx b/atomizer-dashboard/frontend/src/components/canvas/panels/IntrospectionPanel.tsx index 1b87dca4..633d5e23 100644 --- a/atomizer-dashboard/frontend/src/components/canvas/panels/IntrospectionPanel.tsx +++ b/atomizer-dashboard/frontend/src/components/canvas/panels/IntrospectionPanel.tsx @@ -2,7 +2,7 @@ * Introspection Panel - Shows discovered expressions and extractors from NX model */ -import { useState, useEffect, useCallback } from 'react'; +import { useState, useEffect, useCallback, useMemo } from 'react'; import { X, Search, @@ -27,9 +27,11 @@ interface IntrospectionPanelProps { interface Expression { name: string; value: number; + rhs?: string; min?: number; max?: number; - unit: string; + unit?: string; + units?: string; // API returns 'units' not 'unit' type: string; source?: string; } @@ -47,14 +49,34 @@ interface DependentFile { name: string; } +// The API returns expressions in a nested structure +interface ExpressionsResult { + user: Expression[]; + internal: Expression[]; + total_count: number; + user_count: number; +} + interface IntrospectionResult { - file_path: string; - file_type: string; - expressions: Expression[]; - solver_type: string | null; - dependent_files: DependentFile[]; - extractors_available: Extractor[]; - warnings: string[]; + part_file?: string; + part_path?: string; + file_path?: string; + file_type?: string; + success?: boolean; + error?: string | null; + // Expressions can be either an array (old format) or object with user/internal (new format) + expressions: Expression[] | ExpressionsResult; + solver_type?: string | null; + dependent_files?: DependentFile[]; + extractors_available?: Extractor[]; + warnings?: string[]; + // Additional fields from NX introspection + mass_properties?: Record; + materials?: Record; + bodies?: Record; + attributes?: Array<{ title: string; value: string }>; + units?: Record; + linked_parts?: Record; } export function IntrospectionPanel({ filePath, studyId, onClose }: IntrospectionPanelProps) { @@ -161,10 +183,23 @@ export function IntrospectionPanel({ filePath, studyId, onClose }: Introspection }); }; - const filteredExpressions = - result?.expressions.filter((e) => - e.name.toLowerCase().includes(searchTerm.toLowerCase()) - ) || []; + // Handle both array format (old) and object format (new API) + const allExpressions: Expression[] = useMemo(() => { + if (!result?.expressions) return []; + + // Check if expressions is an array (old format) or object (new format) + if (Array.isArray(result.expressions)) { + return result.expressions; + } + + // New format: { user: [...], internal: [...] } + const exprObj = result.expressions as ExpressionsResult; + return [...(exprObj.user || []), ...(exprObj.internal || [])]; + }, [result?.expressions]); + + const filteredExpressions = allExpressions.filter((e) => + e.name.toLowerCase().includes(searchTerm.toLowerCase()) + ); return (
@@ -260,7 +295,7 @@ export function IntrospectionPanel({ filePath, studyId, onClose }: Introspection

{expr.name}

- {expr.value} {expr.unit} + {expr.value} {expr.units || expr.unit || ''} {expr.source === 'inferred' && ( (inferred) )} @@ -281,7 +316,8 @@ export function IntrospectionPanel({ filePath, studyId, onClose }: Introspection )}

- {/* Extractors Section */} + {/* Extractors Section - only show if available */} + {(result.extractors_available?.length ?? 0) > 0 && (