diff --git a/atomizer-dashboard/frontend/src/components/canvas/nodes/AlgorithmNode.tsx b/atomizer-dashboard/frontend/src/components/canvas/nodes/AlgorithmNode.tsx index b2aaee33..84da65b8 100644 --- a/atomizer-dashboard/frontend/src/components/canvas/nodes/AlgorithmNode.tsx +++ b/atomizer-dashboard/frontend/src/components/canvas/nodes/AlgorithmNode.tsx @@ -1,16 +1,14 @@ import { memo } from 'react'; import { NodeProps } from 'reactflow'; +import { BrainCircuit } from 'lucide-react'; import { BaseNode } from './BaseNode'; import { AlgorithmNodeData } from '../../../lib/canvas/schema'; function AlgorithmNodeComponent(props: NodeProps) { const { data } = props; return ( - 🧠} color="text-indigo-600" colorBg="bg-indigo-50"> - {data.method &&
{data.method}
} - {data.maxTrials && ( -
{data.maxTrials} trials
- )} + } iconColor="text-indigo-400"> + {data.method ? `${data.method} (${data.maxTrials || 100} trials)` : 'Select method'} ); } diff --git a/atomizer-dashboard/frontend/src/components/canvas/nodes/BaseNode.tsx b/atomizer-dashboard/frontend/src/components/canvas/nodes/BaseNode.tsx index a027c70c..171ca3bf 100644 --- a/atomizer-dashboard/frontend/src/components/canvas/nodes/BaseNode.tsx +++ b/atomizer-dashboard/frontend/src/components/canvas/nodes/BaseNode.tsx @@ -1,11 +1,11 @@ import { memo, ReactNode } from 'react'; import { Handle, Position, NodeProps } from 'reactflow'; +import { AlertCircle } from 'lucide-react'; import { BaseNodeData } from '../../../lib/canvas/schema'; interface BaseNodeProps extends NodeProps { icon: ReactNode; - color: string; - colorBg?: string; + iconColor: string; children?: ReactNode; inputs?: number; outputs?: number; @@ -15,127 +15,63 @@ function BaseNodeComponent({ data, selected, icon, - color, - colorBg = 'bg-dark-700', + iconColor, children, inputs = 1, outputs = 1, }: BaseNodeProps) { - const hasError = data.errors && data.errors.length > 0; - const isConfigured = data.configured; + const hasErrors = data.errors && data.errors.length > 0; return (
- {/* Glow effect on selection */} - {selected && ( -
- )} - - {/* Input handles */} {inputs > 0 && ( )} - {/* Header */} -
-
- {icon} +
+
+ {icon}
-
- {data.label} - {!isConfigured && ( - Needs config - )} +
+
{data.label}
+ {!data.configured && ( +
+ )}
- {/* Content */} {children && ( -
+
{children}
)} - {/* Status indicator */} -
- {hasError ? ( - - ) : isConfigured ? ( - - ) : ( - - )} -
- - {/* Errors */} - {hasError && ( -
-

- - {data.errors?.[0]} -

+ {hasErrors && ( +
+ + {data.errors![0]}
)} - {/* Output handles */} {outputs > 0 && ( )} - - {/* Inline styles for animation */} -
); } diff --git a/atomizer-dashboard/frontend/src/components/canvas/nodes/ConstraintNode.tsx b/atomizer-dashboard/frontend/src/components/canvas/nodes/ConstraintNode.tsx index 2a5c0c7b..e7a69eec 100644 --- a/atomizer-dashboard/frontend/src/components/canvas/nodes/ConstraintNode.tsx +++ b/atomizer-dashboard/frontend/src/components/canvas/nodes/ConstraintNode.tsx @@ -1,18 +1,16 @@ import { memo } from 'react'; import { NodeProps } from 'reactflow'; +import { ShieldAlert } from 'lucide-react'; import { BaseNode } from './BaseNode'; import { ConstraintNodeData } from '../../../lib/canvas/schema'; function ConstraintNodeComponent(props: NodeProps) { const { data } = props; return ( - 🚧} color="text-orange-600" colorBg="bg-orange-50"> - {data.name &&
{data.name}
} - {data.operator && data.value !== undefined && ( -
- {data.operator} {data.value} -
- )} + } iconColor="text-amber-400"> + {data.name && data.operator && data.value !== undefined + ? `${data.name} ${data.operator} ${data.value}` + : 'Set constraint'} ); } diff --git a/atomizer-dashboard/frontend/src/components/canvas/nodes/DesignVarNode.tsx b/atomizer-dashboard/frontend/src/components/canvas/nodes/DesignVarNode.tsx index 4007b90c..c36a39a0 100644 --- a/atomizer-dashboard/frontend/src/components/canvas/nodes/DesignVarNode.tsx +++ b/atomizer-dashboard/frontend/src/components/canvas/nodes/DesignVarNode.tsx @@ -1,17 +1,17 @@ import { memo } from 'react'; import { NodeProps } from 'reactflow'; +import { SlidersHorizontal } from 'lucide-react'; import { BaseNode } from './BaseNode'; import { DesignVarNodeData } from '../../../lib/canvas/schema'; function DesignVarNodeComponent(props: NodeProps) { const { data } = props; return ( - 📐} color="text-green-600" colorBg="bg-green-50"> - {data.expressionName &&
{data.expressionName}
} - {data.minValue !== undefined && data.maxValue !== undefined && ( -
- [{data.minValue} - {data.maxValue}] {data.unit || ''} -
+ } iconColor="text-emerald-400"> + {data.expressionName ? ( + {data.expressionName} + ) : ( + 'Select expression' )} ); diff --git a/atomizer-dashboard/frontend/src/components/canvas/nodes/ExtractorNode.tsx b/atomizer-dashboard/frontend/src/components/canvas/nodes/ExtractorNode.tsx index 108eb327..d577a0cc 100644 --- a/atomizer-dashboard/frontend/src/components/canvas/nodes/ExtractorNode.tsx +++ b/atomizer-dashboard/frontend/src/components/canvas/nodes/ExtractorNode.tsx @@ -1,16 +1,14 @@ import { memo } from 'react'; import { NodeProps } from 'reactflow'; +import { FlaskConical } from 'lucide-react'; import { BaseNode } from './BaseNode'; import { ExtractorNodeData } from '../../../lib/canvas/schema'; function ExtractorNodeComponent(props: NodeProps) { const { data } = props; return ( - 🔬} color="text-cyan-600" colorBg="bg-cyan-50"> - {data.extractorName &&
{data.extractorName}
} - {data.extractorId && ( -
{data.extractorId}
- )} + } iconColor="text-cyan-400"> + {data.extractorName || 'Select extractor'} ); } diff --git a/atomizer-dashboard/frontend/src/components/canvas/nodes/ModelNode.tsx b/atomizer-dashboard/frontend/src/components/canvas/nodes/ModelNode.tsx index 24707b39..db454ada 100644 --- a/atomizer-dashboard/frontend/src/components/canvas/nodes/ModelNode.tsx +++ b/atomizer-dashboard/frontend/src/components/canvas/nodes/ModelNode.tsx @@ -1,18 +1,14 @@ import { memo } from 'react'; import { NodeProps } from 'reactflow'; +import { Box } from 'lucide-react'; import { BaseNode } from './BaseNode'; import { ModelNodeData } from '../../../lib/canvas/schema'; function ModelNodeComponent(props: NodeProps) { const { data } = props; return ( - 📦} color="text-blue-600" colorBg="bg-blue-50" inputs={0}> - {data.filePath && ( -
{data.filePath.split('/').pop()}
- )} - {data.fileType && ( -
{data.fileType.toUpperCase()}
- )} + } iconColor="text-blue-400" inputs={0}> + {data.filePath ? data.filePath.split(/[/\\]/).pop() : 'No file selected'} ); } diff --git a/atomizer-dashboard/frontend/src/components/canvas/nodes/ObjectiveNode.tsx b/atomizer-dashboard/frontend/src/components/canvas/nodes/ObjectiveNode.tsx index 77acd42f..6a48097d 100644 --- a/atomizer-dashboard/frontend/src/components/canvas/nodes/ObjectiveNode.tsx +++ b/atomizer-dashboard/frontend/src/components/canvas/nodes/ObjectiveNode.tsx @@ -1,19 +1,14 @@ import { memo } from 'react'; import { NodeProps } from 'reactflow'; +import { Target } from 'lucide-react'; import { BaseNode } from './BaseNode'; import { ObjectiveNodeData } from '../../../lib/canvas/schema'; function ObjectiveNodeComponent(props: NodeProps) { const { data } = props; return ( - 🎯} color="text-red-600" colorBg="bg-red-50"> - {data.name &&
{data.name}
} - {data.direction && ( -
- {data.direction === 'minimize' ? '↓ Minimize' : '↑ Maximize'} - {data.weight !== 1 && ` (w=${data.weight})`} -
- )} + } iconColor="text-rose-400"> + {data.name ? `${data.direction === 'maximize' ? '↑' : '↓'} ${data.name}` : 'Set objective'} ); } diff --git a/atomizer-dashboard/frontend/src/components/canvas/nodes/SolverNode.tsx b/atomizer-dashboard/frontend/src/components/canvas/nodes/SolverNode.tsx index dfa08559..2ecef9a8 100644 --- a/atomizer-dashboard/frontend/src/components/canvas/nodes/SolverNode.tsx +++ b/atomizer-dashboard/frontend/src/components/canvas/nodes/SolverNode.tsx @@ -1,13 +1,14 @@ import { memo } from 'react'; import { NodeProps } from 'reactflow'; +import { Cpu } from 'lucide-react'; import { BaseNode } from './BaseNode'; import { SolverNodeData } from '../../../lib/canvas/schema'; function SolverNodeComponent(props: NodeProps) { const { data } = props; return ( - ⚙️} color="text-purple-600" colorBg="bg-purple-50"> - {data.solverType &&
{data.solverType}
} + } iconColor="text-violet-400"> + {data.solverType || 'Select solution'} ); } diff --git a/atomizer-dashboard/frontend/src/components/canvas/nodes/SurrogateNode.tsx b/atomizer-dashboard/frontend/src/components/canvas/nodes/SurrogateNode.tsx index a6272ec8..4026d7f6 100644 --- a/atomizer-dashboard/frontend/src/components/canvas/nodes/SurrogateNode.tsx +++ b/atomizer-dashboard/frontend/src/components/canvas/nodes/SurrogateNode.tsx @@ -1,16 +1,14 @@ import { memo } from 'react'; import { NodeProps } from 'reactflow'; +import { Rocket } from 'lucide-react'; import { BaseNode } from './BaseNode'; import { SurrogateNodeData } from '../../../lib/canvas/schema'; function SurrogateNodeComponent(props: NodeProps) { const { data } = props; return ( - 🚀} color="text-pink-600" colorBg="bg-pink-50" outputs={0}> -
{data.enabled ? 'Enabled' : 'Disabled'}
- {data.enabled && data.modelType && ( -
{data.modelType}
- )} + } iconColor="text-pink-400" outputs={0}> + {data.enabled ? (data.modelType || 'Auto') : 'Disabled'} ); } diff --git a/atomizer-dashboard/frontend/src/components/canvas/palette/NodePalette.tsx b/atomizer-dashboard/frontend/src/components/canvas/palette/NodePalette.tsx index d41ffe43..fb203e11 100644 --- a/atomizer-dashboard/frontend/src/components/canvas/palette/NodePalette.tsx +++ b/atomizer-dashboard/frontend/src/components/canvas/palette/NodePalette.tsx @@ -1,22 +1,33 @@ import { DragEvent } from 'react'; import { NodeType } from '../../../lib/canvas/schema'; +import { + Box, + Cpu, + SlidersHorizontal, + FlaskConical, + Target, + ShieldAlert, + BrainCircuit, + Rocket, +} from 'lucide-react'; interface PaletteItem { type: NodeType; label: string; - icon: string; + icon: React.ReactNode; description: string; + color: string; } const PALETTE_ITEMS: PaletteItem[] = [ - { type: 'model', label: 'Model', icon: '📦', description: 'NX model file' }, - { type: 'solver', label: 'Solver', icon: '⚙️', description: 'Nastran solution' }, - { type: 'designVar', label: 'Design Variable', icon: '📐', description: 'Parameter to vary' }, - { type: 'extractor', label: 'Extractor', icon: '🔬', description: 'Physics extraction' }, - { type: 'objective', label: 'Objective', icon: '🎯', description: 'Optimization goal' }, - { type: 'constraint', label: 'Constraint', icon: '🚧', description: 'Limit condition' }, - { type: 'algorithm', label: 'Algorithm', icon: '🧠', description: 'Optimization method' }, - { type: 'surrogate', label: 'Surrogate', icon: '🚀', description: 'Neural acceleration' }, + { type: 'model', label: 'Model', icon: , description: 'NX model file (.prt, .sim)', color: 'text-blue-400' }, + { type: 'solver', label: 'Solver', icon: , description: 'Nastran solution type', color: 'text-violet-400' }, + { type: 'designVar', label: 'Design Variable', icon: , description: 'Parameter to optimize', color: 'text-emerald-400' }, + { type: 'extractor', label: 'Extractor', icon: , description: 'Physics result extraction', color: 'text-cyan-400' }, + { type: 'objective', label: 'Objective', icon: , description: 'Optimization goal', color: 'text-rose-400' }, + { type: 'constraint', label: 'Constraint', icon: , description: 'Design constraint', color: 'text-amber-400' }, + { type: 'algorithm', label: 'Algorithm', icon: , description: 'Optimization method', color: 'text-indigo-400' }, + { type: 'surrogate', label: 'Surrogate', icon: , description: 'Neural acceleration', color: 'text-pink-400' }, ]; export function NodePalette() { @@ -26,24 +37,31 @@ export function NodePalette() { }; return ( -
-

- Components -

-
+
+
+

+ Components +

+

+ Drag to canvas +

+
+
{PALETTE_ITEMS.map((item) => (
onDragStart(e, item.type)} - className="flex items-center gap-3 p-3 bg-dark-800 rounded-lg border border-dark-600 - cursor-grab hover:border-primary-500/50 hover:bg-dark-750 transition-all - active:cursor-grabbing" + className="flex items-center gap-2.5 px-3 py-2.5 bg-dark-800/50 rounded-lg border border-dark-700/50 + cursor-grab hover:border-primary-500/50 hover:bg-dark-800 + active:cursor-grabbing transition-all group" > - {item.icon} -
-
{item.label}
-
{item.description}
+
+ {item.icon} +
+
+
{item.label}
+
{item.description}
))} diff --git a/atomizer-dashboard/frontend/src/lib/canvas/templates.ts b/atomizer-dashboard/frontend/src/lib/canvas/templates.ts index 113bdfce..dc785a64 100644 --- a/atomizer-dashboard/frontend/src/lib/canvas/templates.ts +++ b/atomizer-dashboard/frontend/src/lib/canvas/templates.ts @@ -24,7 +24,7 @@ const massMinimizationTemplate: CanvasTemplate = { name: 'Mass Minimization', description: 'Minimize structural mass while maintaining stiffness constraints. Ideal for brackets, housings, and weight-critical components.', category: 'structural', - icon: '⚖️', + icon: 'Scale', intent: { version: '1.0', source: 'canvas', @@ -67,7 +67,7 @@ const multiObjectiveTemplate: CanvasTemplate = { name: 'Multi-Objective Pareto', description: 'Explore trade-offs between competing objectives. Generates a Pareto front for informed decision-making.', category: 'structural', - icon: '📊', + icon: 'BarChart3', intent: { version: '1.0', source: 'canvas', @@ -112,7 +112,7 @@ const turboModeTemplate: CanvasTemplate = { name: 'Turbo Mode', description: 'Accelerated optimization using neural network surrogates. Run thousands of virtual trials with periodic FEA validation.', category: 'general', - icon: '🚀', + icon: 'Rocket', intent: { version: '1.0', source: 'canvas', @@ -158,7 +158,7 @@ const mirrorZernikeTemplate: CanvasTemplate = { name: 'Mirror Zernike', description: 'Optimize optical mirror surface quality using Zernike wavefront error metrics. Specialized for precision optics.', category: 'optical', - icon: '🔭', + icon: 'Telescope', intent: { version: '1.0', source: 'canvas', @@ -208,7 +208,7 @@ const frequencyOptimizationTemplate: CanvasTemplate = { name: 'Frequency Optimization', description: 'Maximize natural frequencies to avoid resonance. Essential for rotating machinery and vibration-sensitive equipment.', category: 'structural', - icon: '📈', + icon: 'TrendingUp', intent: { version: '1.0', source: 'canvas',