Canvas Builder Visual Updates: - Update all Canvas components to use Atomaster dark theme - BaseNode: dark background (bg-dark-800), white text, primary selection glow - NodePalette: dark sidebar with hover states - NodeConfigPanel: dark inputs, labels, and panel background - ValidationPanel: semi-transparent error/warning panels with backdrop blur - ChatPanel: dark message area with themed welcome state - ExecuteDialog: dark modal with primary button styling - ConfigImporter: dark tabs, inputs, and file upload zone - TemplateSelector: dark cards with category pills and hover effects Setup Page Integration: - Add Configuration/Canvas Builder tab switcher - Canvas tab renders AtomizerCanvas full-height - Tabs styled to match Atomaster theme Build: Passes TypeScript and Vite build Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
54 lines
2.1 KiB
TypeScript
54 lines
2.1 KiB
TypeScript
import { DragEvent } from 'react';
|
|
import { NodeType } from '../../../lib/canvas/schema';
|
|
|
|
interface PaletteItem {
|
|
type: NodeType;
|
|
label: string;
|
|
icon: string;
|
|
description: 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' },
|
|
];
|
|
|
|
export function NodePalette() {
|
|
const onDragStart = (event: DragEvent, nodeType: NodeType) => {
|
|
event.dataTransfer.setData('application/reactflow', nodeType);
|
|
event.dataTransfer.effectAllowed = 'move';
|
|
};
|
|
|
|
return (
|
|
<div className="w-64 bg-dark-850 border-r border-dark-700 p-4 overflow-y-auto">
|
|
<h3 className="text-sm font-semibold text-dark-400 uppercase mb-4 tracking-wide">
|
|
Components
|
|
</h3>
|
|
<div className="space-y-2">
|
|
{PALETTE_ITEMS.map((item) => (
|
|
<div
|
|
key={item.type}
|
|
draggable
|
|
onDragStart={(e) => 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"
|
|
>
|
|
<span className="text-xl">{item.icon}</span>
|
|
<div>
|
|
<div className="font-medium text-white">{item.label}</div>
|
|
<div className="text-xs text-dark-400">{item.description}</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|