Spaces:
Paused
Paused
| // Initialize Konva stage | |
| const stage = new Konva.Stage({ | |
| container: 'container', | |
| width: 1000, | |
| height: 600 | |
| }); | |
| const layer = new Konva.Layer(); | |
| stage.add(layer); | |
| // Store nodes, connections, and parsed data | |
| let nodes = []; | |
| let connections = []; | |
| let parsedConnections = []; | |
| let selectedNode = null; | |
| // Submit code or file for parsing | |
| function submitCode() { | |
| const fileInput = document.getElementById('codeFile'); | |
| const codeInput = document.getElementById('codeInput').value; | |
| const formData = new FormData(); | |
| if (fileInput.files.length > 0) { | |
| formData.append('file', fileInput.files[0]); | |
| } else if (codeInput) { | |
| formData.append('code', codeInput); | |
| } else { | |
| alert('Please upload a file or paste code.'); | |
| return; | |
| } | |
| fetch('/parse_code', { | |
| method: 'POST', | |
| body: formData | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| if (data.error) { | |
| alert(data.error); | |
| return; | |
| } | |
| clearCanvas(); | |
| parsedConnections = data.connections; // Store for auto-connect | |
| createNodesFromParsedData(data.nodes, data.connections); | |
| }) | |
| .catch(error => console.error('Error:', error)); | |
| } | |
| // Clear existing nodes and connections | |
| function clearCanvas() { | |
| nodes.forEach(node => node.destroy()); | |
| layer.find('Shape').forEach(shape => shape.destroy()); // Includes Bezier curves | |
| nodes = []; | |
| connections = []; | |
| parsedConnections = []; | |
| layer.draw(); | |
| } | |
| // Create nodes and connections from parsed data | |
| function createNodesFromParsedData(parsedNodes, parsedConnections) { | |
| parsedNodes.forEach(nodeData => { | |
| const node = createNode( | |
| nodeData.x, | |
| nodeData.y, | |
| nodeData.label, | |
| nodeData.type, | |
| nodeData.inputs, | |
| nodeData.outputs, | |
| nodeData.id | |
| ); | |
| nodes.push(node); | |
| layer.add(node); | |
| }); | |
| // Initially, don't draw connections; wait for auto-connect or manual | |
| layer.draw(); | |
| saveNodes(); | |
| } | |
| // Create a node with inputs and outputs | |
| function createNode(x, y, label, type, inputs = [], outputs = [], id) { | |
| const node = new Konva.Group({ | |
| x: x, | |
| y: y, | |
| draggable: true | |
| }); | |
| // Node rectangle | |
| const color = type === 'function' ? '#ffeb3b' : type.includes('variable') ? '#90caf9' : type === 'import' ? '#a5d6a7' : '#ccc'; | |
| const box = new Konva.Rect({ | |
| width: 100, | |
| height: 50, | |
| fill: color, | |
| stroke: 'black', | |
| strokeWidth: 2, | |
| cornerRadius: 5 | |
| }); | |
| // Node label | |
| const text = new Konva.Text({ | |
| text: label, | |
| fontSize: 12, | |
| fontFamily: 'Arial', | |
| fill: 'black', | |
| width: 100, | |
| align: 'center', | |
| y: 20 | |
| }); | |
| node.add(box); | |
| node.add(text); | |
| // Add input/output ports | |
| inputs.forEach((input, i) => { | |
| const circle = new Konva.Circle({ | |
| x: 0, | |
| y: 10 + i * 20, | |
| radius: 5, | |
| fill: 'red' | |
| }); | |
| node.add(circle); | |
| }); | |
| outputs.forEach((output, i) => { | |
| const circle = new Konva.Circle({ | |
| x: 100, | |
| y: 10 + i * 20, | |
| radius: 5, | |
| fill: 'green' | |
| }); | |
| node.add(circle); | |
| }); | |
| // Node data | |
| node.data = { | |
| id: id, | |
| type: type, | |
| label: label, | |
| inputs: inputs, | |
| outputs: outputs, | |
| x: x, | |
| y: y | |
| }; | |
| // Handle node click for manual connections | |
| node.on('click', () => { | |
| if (!selectedNode) { | |
| selectedNode = node; | |
| } else { | |
| createSplineConnection(selectedNode, node); | |
| connections.push({ | |
| from: selectedNode.data.id, | |
| to: node.data.id | |
| }); | |
| selectedNode = null; | |
| saveNodes(); | |
| } | |
| }); | |
| // Update position and connections on drag | |
| node.on('dragmove', () => { | |
| node.data.x = node.x(); | |
| node.data.y = node.y(); | |
| updateConnections(); | |
| saveNodes(); | |
| }); | |
| return node; | |
| } | |
| // Create a spline (Bezier curve) connection | |
| function createSplineConnection(fromNode, toNode) { | |
| const startX = fromNode.x() + 100; | |
| const startY = fromNode.y() + 25; | |
| const endX = toNode.x(); | |
| const endY = toNode.y() + 25; | |
| // Control points for Bezier curve | |
| const control1X = startX + (endX - startX) / 3; | |
| const control1Y = startY; | |
| const control2X = startX + 2 * (endX - startX) / 3; | |
| const control2Y = endY; | |
| const spline = new Konva.Shape({ | |
| sceneFunc: function(context, shape) { | |
| context.beginPath(); | |
| context.moveTo(startX, startY); | |
| context.bezierCurveTo(control1X, control1Y, control2X, control2Y, endX, endY); | |
| context.fillStrokeShape(shape); | |
| }, | |
| stroke: 'black', | |
| strokeWidth: 2 | |
| }); | |
| spline.data = { from: fromNode.data.id, to: toNode.data.id }; | |
| layer.add(spline); | |
| layer.draw(); | |
| } | |
| // Auto-connect nodes based on parsed connections | |
| function autoConnect() { | |
| // Clear existing connections | |
| layer.find('Shape').forEach(shape => { | |
| if (shape.data && shape.data.from !== undefined) { | |
| shape.destroy(); | |
| } | |
| }); | |
| connections = []; | |
| // Create spline connections for parsed data | |
| parsedConnections.forEach(conn => { | |
| const fromNode = nodes.find(n => n.data.id === conn.from); | |
| const toNode = nodes.find(n => n.data.id === conn.to); | |
| if (fromNode && toNode) { | |
| createSplineConnection(fromNode, toNode); | |
| connections.push({ from: conn.from, to: conn.to }); | |
| } | |
| }); | |
| layer.draw(); | |
| saveNodes(); | |
| } | |
| // Add a manual node | |
| function addNode() { | |
| const node = createNode( | |
| Math.random() * (stage.width() - 100), | |
| Math.random() * (stage.height() - 100), | |
| 'Function', | |
| 'function', | |
| [], | |
| [], | |
| nodes.length | |
| ); | |
| nodes.push(node); | |
| layer.add(node); | |
| layer.draw(); | |
| saveNodes(); | |
| } | |
| // Update spline connections when nodes move | |
| function updateConnections() { | |
| layer.find('Shape').forEach(shape => { | |
| if (shape.data && shape.data.from !== undefined) { | |
| const fromNode = nodes.find(n => n.data.id === shape.data.from); | |
| const toNode = nodes.find(n => n.data.id === shape.data.to); | |
| if (fromNode && toNode) { | |
| const startX = fromNode.x() + 100; | |
| const startY = fromNode.y() + 25; | |
| const endX = toNode.x(); | |
| const endY = toNode.y() + 25; | |
| const control1X = startX + (endX - startX) / 3; | |
| const control1Y = startY; | |
| const control2X = startX + 2 * (endX - startX) / 3; | |
| const control2Y = endY; | |
| shape.sceneFunc(function(context, shape) { | |
| context.beginPath(); | |
| context.moveTo(startX, startY); | |
| context.bezierCurveTo(control1X, control1Y, control2X, control2Y, endX, endY); | |
| context.fillStrokeShape(shape); | |
| }); | |
| } | |
| } | |
| }); | |
| layer.draw(); | |
| } | |
| // Save nodes and connections to backend | |
| function saveNodes() { | |
| fetch('/save_nodes', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ | |
| nodes: nodes.map(n => n.data), | |
| connections: connections | |
| }) | |
| }).then(response => response.json()) | |
| .then(data => console.log('Saved:', data)) | |
| .catch(error => console.error('Error:', error)); | |
| } | |
| // Initial draw | |
| layer.draw(); |