feat: Add sub-part introspection and existing FEA results UI
Backend:
- GET /nx/parts - List all .prt files in model directory
- GET /nx/introspect/{part_name} - Introspect a specific part file
(e.g., M1_Blank.prt instead of just the assembly)
- Each part gets its own cache file (_introspection_{stem}.json)
Frontend IntrospectionPanel:
- Add 'FEA Results' section showing existing OP2/F06 sources
- Green checkmark when results exist, shows recommended source
- Expand file_deps and fea_results sections by default
- Add CheckCircle2 and Database icons
This allows introspecting component parts that contain the actual
design variable expressions (e.g., M1_Blank has 56 expressions
while the assembly ASSY_M1 only has 5).
This commit is contained in:
@@ -21,6 +21,8 @@ import {
|
||||
Settings2,
|
||||
GitBranch,
|
||||
File,
|
||||
Database,
|
||||
CheckCircle2,
|
||||
} from 'lucide-react';
|
||||
import { useCanvasStore } from '../../../hooks/useCanvasStore';
|
||||
|
||||
@@ -101,6 +103,22 @@ interface IntrospectionResult {
|
||||
units?: Record<string, unknown>;
|
||||
linked_parts?: Record<string, unknown>;
|
||||
file_dependencies?: FileDependencies;
|
||||
existing_fea_results?: {
|
||||
has_results: boolean;
|
||||
sources: Array<{
|
||||
location: string;
|
||||
path: string;
|
||||
op2: string[];
|
||||
f06: string[];
|
||||
bdf: string[];
|
||||
timestamp?: number;
|
||||
}>;
|
||||
recommended?: {
|
||||
location: string;
|
||||
path: string;
|
||||
op2: string[];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// Baseline run result interface
|
||||
@@ -125,7 +143,7 @@ export function IntrospectionPanel({ filePath, studyId, onClose }: Introspection
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [expandedSections, setExpandedSections] = useState<Set<string>>(
|
||||
new Set(['expressions', 'extractors'])
|
||||
new Set(['expressions', 'extractors', 'file_deps', 'fea_results'])
|
||||
);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
|
||||
@@ -783,6 +801,77 @@ export function IntrospectionPanel({ filePath, studyId, onClose }: Introspection
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Existing FEA Results Section */}
|
||||
{result.existing_fea_results && (
|
||||
<div className="border border-dark-700 rounded-lg overflow-hidden">
|
||||
<button
|
||||
onClick={() => toggleSection('fea_results')}
|
||||
className="w-full flex items-center justify-between px-3 py-2 bg-dark-800 hover:bg-dark-750 transition-colors"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{result.existing_fea_results.has_results ? (
|
||||
<CheckCircle2 size={14} className="text-green-400" />
|
||||
) : (
|
||||
<Database size={14} className="text-dark-500" />
|
||||
)}
|
||||
<span className="text-sm font-medium text-white">
|
||||
FEA Results {result.existing_fea_results.has_results ?
|
||||
`(${result.existing_fea_results.sources?.length || 0} sources)` :
|
||||
'(none)'}
|
||||
</span>
|
||||
</div>
|
||||
{expandedSections.has('fea_results') ? (
|
||||
<ChevronDown size={14} className="text-dark-400" />
|
||||
) : (
|
||||
<ChevronRight size={14} className="text-dark-400" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
{expandedSections.has('fea_results') && (
|
||||
<div className="p-2 space-y-2">
|
||||
{result.existing_fea_results.has_results ? (
|
||||
<>
|
||||
<p className="text-xs text-green-400 mb-2">
|
||||
Existing results found - no solve needed for extraction
|
||||
</p>
|
||||
{result.existing_fea_results.sources?.map((source, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className={`p-2 rounded text-xs ${
|
||||
source.location === result.existing_fea_results?.recommended?.location
|
||||
? 'bg-green-500/10 border border-green-500/30'
|
||||
: 'bg-dark-850'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<span className="text-white font-medium">{source.location}</span>
|
||||
{source.location === result.existing_fea_results?.recommended?.location && (
|
||||
<span className="text-[10px] text-green-400 bg-green-500/20 px-1 rounded">
|
||||
recommended
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-dark-400 space-y-0.5">
|
||||
{source.op2?.length > 0 && (
|
||||
<p>OP2: {source.op2.join(', ')}</p>
|
||||
)}
|
||||
{source.f06?.length > 0 && (
|
||||
<p>F06: {source.f06.join(', ')}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<p className="text-xs text-dark-500 text-center py-2">
|
||||
No FEA results found. Run baseline to generate results.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Extractors Section - only show if available */}
|
||||
{(result.extractors_available?.length ?? 0) > 0 && (
|
||||
<div className="border border-dark-700 rounded-lg overflow-hidden">
|
||||
|
||||
Reference in New Issue
Block a user