fix(canvas): Bug fixes for node movement, drag-drop, config panel, and introspection

- SpecRenderer: Add localNodes state with applyNodeChanges for smooth node dragging
- SpecRenderer: Fix getDefaultNodeData() - extractor uses 'custom_function' type with function definition
- SpecRenderer: Fix constraint default - use constraint_type instead of type
- CanvasView: Show config panel INSTEAD of chat when node selected (not blocked)
- NodeConfigPanelV2: Enable showHeader for code editor toolbar (Generate/Snippets/Validate/Test buttons)
- NodeConfigPanelV2: Pass studyId to IntrospectionPanel
- IntrospectionPanel: Accept studyId prop and use correct API endpoint
- optimization.py: Search multiple directories for model files including 1_setup/model/
This commit is contained in:
2026-01-20 14:14:14 -05:00
parent cf8c57fdac
commit 47f8b50112
5 changed files with 1214 additions and 922 deletions

View File

@@ -10,7 +10,7 @@
* P2.7-P2.10: SpecRenderer component with node/edge/selection handling
*/
import { useCallback, useRef, useEffect, useMemo, DragEvent } from 'react';
import { useCallback, useRef, useEffect, useMemo, useState, DragEvent } from 'react';
import ReactFlow, {
Background,
Controls,
@@ -22,6 +22,7 @@ import ReactFlow, {
NodeChange,
EdgeChange,
Connection,
applyNodeChanges,
} from 'reactflow';
import 'reactflow/dist/style.css';
@@ -74,8 +75,28 @@ function getDefaultNodeData(type: AddableNodeType, position: { x: number; y: num
case 'extractor':
return {
name: `extractor_${timestamp}`,
type: 'custom',
type: 'custom_function', // Must be valid ExtractorType
builtin: false,
enabled: true,
// Custom function extractors need a function definition
function: {
name: 'extract',
source_code: `def extract(op2_path: str, config: dict = None) -> dict:
"""
Custom extractor function.
Args:
op2_path: Path to the OP2 results file
config: Optional configuration dict
Returns:
Dictionary with extracted values
"""
# TODO: Implement extraction logic
return {'value': 0.0}
`,
},
outputs: [{ name: 'value', metric: 'custom' }],
canvas_position: position,
};
case 'objective':
@@ -90,7 +111,8 @@ function getDefaultNodeData(type: AddableNodeType, position: { x: number; y: num
case 'constraint':
return {
name: `constraint_${timestamp}`,
type: 'upper',
constraint_type: 'hard', // Must be 'hard' or 'soft'
operator: '<=',
limit: 1.0,
source_extractor_id: null,
source_output: null,
@@ -208,12 +230,23 @@ function SpecRendererInner({
nodesRef.current = nodes;
}, [nodes]);
// Track local node state for smooth dragging
const [localNodes, setLocalNodes] = useState(nodes);
// Sync local nodes with spec-derived nodes when spec changes
useEffect(() => {
setLocalNodes(nodes);
}, [nodes]);
// Handle node position changes
const onNodesChange = useCallback(
(changes: NodeChange[]) => {
if (!editable) return;
// Handle position changes
// Apply changes to local state for smooth dragging
setLocalNodes((nds) => applyNodeChanges(changes, nds));
// Handle position changes - save to spec when drag ends
for (const change of changes) {
if (change.type === 'position' && change.position && change.dragging === false) {
// Dragging ended - update spec
@@ -458,7 +491,7 @@ function SpecRendererInner({
)}
<ReactFlow
nodes={nodes}
nodes={localNodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}