From 91cf9ca1fdd9a59c8a0cb804d044cf57e60c66d9 Mon Sep 17 00:00:00 2001 From: Anto01 Date: Tue, 20 Jan 2026 14:47:09 -0500 Subject: [PATCH] fix(canvas): Add Save/Reload buttons and expand IntrospectionPanel to show all model data CanvasView: - Fix Save button visibility - now shows when spec is loaded (grayed if no changes) - Separate logic for spec mode vs legacy mode save buttons - Fix Reload button visibility IntrospectionPanel: - Add Mass Properties section (mass, volume, surface area, CoG, body count) - Add Linked Parts section showing file dependencies - Add Bodies section (solid/sheet body counts) - Add Units section showing unit system - Type-safe access to all nested properties --- .../canvas/panels/IntrospectionPanel.tsx | 178 ++++++++++++++++++ .../frontend/src/pages/CanvasView.tsx | 29 ++- 2 files changed, 201 insertions(+), 6 deletions(-) diff --git a/atomizer-dashboard/frontend/src/components/canvas/panels/IntrospectionPanel.tsx b/atomizer-dashboard/frontend/src/components/canvas/panels/IntrospectionPanel.tsx index 633d5e23..087f7b33 100644 --- a/atomizer-dashboard/frontend/src/components/canvas/panels/IntrospectionPanel.tsx +++ b/atomizer-dashboard/frontend/src/components/canvas/panels/IntrospectionPanel.tsx @@ -15,6 +15,10 @@ import { FlaskConical, SlidersHorizontal, AlertTriangle, + Scale, + Link, + Box, + Settings2, } from 'lucide-react'; import { useCanvasStore } from '../../../hooks/useCanvasStore'; @@ -316,6 +320,180 @@ export function IntrospectionPanel({ filePath, studyId, onClose }: Introspection )} + {/* Mass Properties Section */} + {result.mass_properties && ( +
+ + + {expandedSections.has('mass') && ( +
+ {result.mass_properties.mass_kg !== undefined && ( +
+ Mass + + {(result.mass_properties.mass_kg as number).toFixed(4)} kg + +
+ )} + {result.mass_properties.volume_mm3 !== undefined && (result.mass_properties.volume_mm3 as number) > 0 && ( +
+ Volume + + {((result.mass_properties.volume_mm3 as number) / 1e9).toFixed(6)} m³ + +
+ )} + {result.mass_properties.surface_area_mm2 !== undefined && (result.mass_properties.surface_area_mm2 as number) > 0 && ( +
+ Surface Area + + {((result.mass_properties.surface_area_mm2 as number) / 1e6).toFixed(4)} m² + +
+ )} + {Array.isArray(result.mass_properties.center_of_gravity_mm) && ( +
+ CoG (mm) + + [{(result.mass_properties.center_of_gravity_mm as number[]).map((v: number) => v.toFixed(1)).join(', ')}] + +
+ )} + {typeof result.mass_properties.num_bodies === 'number' && ( +
+ Bodies + {result.mass_properties.num_bodies} +
+ )} +
+ )} +
+ )} + + {/* Linked Parts / File Dependencies Section */} + {result.linked_parts && ( +
+ + + {expandedSections.has('linked') && ( +
+ {((result.linked_parts.loaded_parts as Array<{name: string, path: string, leaf_name: string}>) || []).map((part) => ( +
+

{part.name}

+

+ {part.leaf_name || part.path.split(/[/\\]/).pop()} +

+
+ ))} + {((result.linked_parts.loaded_parts as Array) || []).length === 0 && ( +

No linked parts found

+ )} +
+ )} +
+ )} + + {/* Bodies Section */} + {result.bodies && (result.bodies.counts as {total: number})?.total > 0 && ( +
+ + + {expandedSections.has('bodies') && ( +
+
+ Solid Bodies + {(result.bodies.counts as {solid: number})?.solid || 0} +
+
+ Sheet Bodies + {(result.bodies.counts as {sheet: number})?.sheet || 0} +
+
+ )} +
+ )} + + {/* Units Section */} + {result.units && ( +
+ + + {expandedSections.has('units') && ( +
+ {(result.units as {base_units?: Record})?.base_units && + Object.entries((result.units as {base_units: Record}).base_units).map(([key, value]) => ( +
+ {key} + {value} +
+ )) + } +
+ )} +
+ )} + {/* Extractors Section - only show if available */} {(result.extractors_available?.length ?? 0) > 0 && (
diff --git a/atomizer-dashboard/frontend/src/pages/CanvasView.tsx b/atomizer-dashboard/frontend/src/pages/CanvasView.tsx index b5784eb0..7189322c 100644 --- a/atomizer-dashboard/frontend/src/pages/CanvasView.tsx +++ b/atomizer-dashboard/frontend/src/pages/CanvasView.tsx @@ -296,17 +296,34 @@ export function CanvasView() { {/* Action Buttons */}
- {/* Save Button - only show when there's a study and changes */} - {activeStudyId && ( + {/* Save Button - always show in spec mode with study, grayed when no changes */} + {useSpecMode && spec && ( + )} + + {/* Legacy Save Button */} + {!useSpecMode && activeStudyId && ( +