broadfield-dev commited on
Commit
79efa38
·
verified ·
1 Parent(s): 36da495

Update static/canvas.js

Browse files
Files changed (1) hide show
  1. static/canvas.js +27 -40
static/canvas.js CHANGED
@@ -8,14 +8,12 @@ const stage = new Konva.Stage({
8
  const layer = new Konva.Layer();
9
  stage.add(layer);
10
 
11
- // Store nodes, connections, and parsed data
12
  let nodes = [];
13
- let connections = []; // { fromNodeId, fromPortId, toNodeId, toPortId }
14
  let parsedConnections = [];
15
- let selectedPort = null; // { node, portId, type: 'input'|'output' }
16
  let disconnectMode = false;
17
 
18
- // Submit code or file for parsing
19
  function submitCode() {
20
  const fileInput = document.getElementById('codeFile');
21
  const codeInput = document.getElementById('codeInput').value;
@@ -41,13 +39,12 @@ function submitCode() {
41
  return;
42
  }
43
  clearCanvas();
44
- parsedConnections = data.connections; // Store for auto-connect
45
  createNodesFromParsedData(data.nodes, data.connections);
46
  })
47
  .catch(error => console.error('Error:', error));
48
  }
49
 
50
- // Clear existing nodes and connections
51
  function clearCanvas() {
52
  nodes.forEach(node => node.destroy());
53
  layer.find('Shape').forEach(shape => shape.destroy());
@@ -57,7 +54,6 @@ function clearCanvas() {
57
  layer.draw();
58
  }
59
 
60
- // Create nodes and connections from parsed data
61
  function createNodesFromParsedData(parsedNodes, parsedConnections) {
62
  parsedNodes.forEach(nodeData => {
63
  const node = createNode(
@@ -67,7 +63,8 @@ function createNodesFromParsedData(parsedNodes, parsedConnections) {
67
  nodeData.type,
68
  nodeData.inputs,
69
  nodeData.outputs,
70
- nodeData.id
 
71
  );
72
  nodes.push(node);
73
  layer.add(node);
@@ -76,40 +73,44 @@ function createNodesFromParsedData(parsedNodes, parsedConnections) {
76
  saveNodes();
77
  }
78
 
79
- // Create a node with inputs and outputs
80
- function createNode(x, y, label, type, inputs = [], outputs = [], id) {
81
  const node = new Konva.Group({
82
  x: x,
83
  y: y,
84
  draggable: true
85
  });
86
 
87
- // Node rectangle
88
- const color = type === 'function' ? '#ffeb3b' : type.includes('variable') ? '#90caf9' : type === 'import' ? '#a5d6a7' : '#ccc';
 
 
 
 
89
  const box = new Konva.Rect({
90
- width: 100,
91
- height: 50,
92
  fill: color,
93
  stroke: 'black',
94
  strokeWidth: 2,
95
  cornerRadius: 5
96
  });
97
 
98
- // Node label
 
99
  const text = new Konva.Text({
100
- text: label,
101
  fontSize: 12,
102
  fontFamily: 'Arial',
103
  fill: 'black',
104
- width: 100,
105
  align: 'center',
106
- y: 20
107
  });
108
 
109
  node.add(box);
110
  node.add(text);
111
 
112
- // Add input/output ports with unique IDs
113
  const inputPorts = inputs.map((input, i) => ({
114
  id: `input-${id}-${i}`,
115
  name: input,
@@ -125,22 +126,20 @@ function createNode(x, y, label, type, inputs = [], outputs = [], id) {
125
  id: `output-${id}-${i}`,
126
  name: output,
127
  circle: new Konva.Circle({
128
- x: 100,
129
  y: 10 + i * 20,
130
  radius: 5,
131
  fill: 'green'
132
  })
133
  }));
134
 
135
- // Add ports to node and set up click handlers
136
  inputPorts.forEach(port => {
137
  node.add(port.circle);
138
  port.circle.on('click', () => {
139
  if (!selectedPort) {
140
  selectedPort = { node, portId: port.id, type: 'input' };
141
- disconnectMode = true; // First click on input enables disconnect mode
142
  } else if (selectedPort.type === 'output' && selectedPort.node !== node) {
143
- // Connect output to input
144
  createSplineConnection(
145
  selectedPort.node,
146
  selectedPort.portId,
@@ -157,8 +156,7 @@ function createNode(x, y, label, type, inputs = [], outputs = [], id) {
157
  disconnectMode = false;
158
  saveNodes();
159
  } else if (disconnectMode && selectedPort.type === 'input' && selectedPort.node === node) {
160
- // Select output to disconnect
161
- selectedPort = { node, portId: port.id, type: 'input' }; // Keep input selected
162
  } else {
163
  selectedPort = null;
164
  disconnectMode = false;
@@ -173,7 +171,6 @@ function createNode(x, y, label, type, inputs = [], outputs = [], id) {
173
  selectedPort = { node, portId: port.id, type: 'output' };
174
  disconnectMode = false;
175
  } else if (disconnectMode && selectedPort.type === 'input') {
176
- // Disconnect input from output
177
  const connIndex = connections.findIndex(
178
  c => c.toNodeId === selectedPort.node.data.id &&
179
  c.toPortId === selectedPort.portId &&
@@ -202,7 +199,6 @@ function createNode(x, y, label, type, inputs = [], outputs = [], id) {
202
  });
203
  });
204
 
205
- // Node data
206
  node.data = {
207
  id: id,
208
  type: type,
@@ -210,10 +206,10 @@ function createNode(x, y, label, type, inputs = [], outputs = [], id) {
210
  inputs: inputPorts,
211
  outputs: outputPorts,
212
  x: x,
213
- y: y
 
214
  };
215
 
216
- // Update position and connections on drag
217
  node.on('dragmove', () => {
218
  node.data.x = node.x();
219
  node.data.y = node.y();
@@ -224,7 +220,6 @@ function createNode(x, y, label, type, inputs = [], outputs = [], id) {
224
  return node;
225
  }
226
 
227
- // Create a spline (Bezier curve) connection
228
  function createSplineConnection(fromNode, fromPortId, toNode, toPortId) {
229
  const fromPort = fromNode.data.outputs.find(p => p.id === fromPortId);
230
  const toPort = toNode.data.inputs.find(p => p.id === toPortId);
@@ -235,7 +230,6 @@ function createSplineConnection(fromNode, fromPortId, toNode, toPortId) {
235
  const endX = toNode.x() + toPort.circle.x();
236
  const endY = toNode.y() + toPort.circle.y();
237
 
238
- // Control points for Bezier curve
239
  const control1X = startX + (endX - startX) / 3;
240
  const control1Y = startY;
241
  const control2X = startX + 2 * (endX - startX) / 3;
@@ -262,9 +256,7 @@ function createSplineConnection(fromNode, fromPortId, toNode, toPortId) {
262
  layer.draw();
263
  }
264
 
265
- // Auto-connect nodes based on parsed connections
266
  function autoConnect() {
267
- // Clear existing connections
268
  layer.find('Shape').forEach(shape => {
269
  if (shape.data && shape.data.fromNodeId !== undefined) {
270
  shape.destroy();
@@ -272,12 +264,10 @@ function autoConnect() {
272
  });
273
  connections = [];
274
 
275
- // Create spline connections for parsed data
276
  parsedConnections.forEach(conn => {
277
  const fromNode = nodes.find(n => n.data.id === conn.from);
278
  const toNode = nodes.find(n => n.data.id === conn.to);
279
  if (fromNode && toNode) {
280
- // Find first available output and input ports
281
  const fromPort = fromNode.data.outputs[0];
282
  const toPort = toNode.data.inputs[0];
283
  if (fromPort && toPort) {
@@ -296,7 +286,6 @@ function autoConnect() {
296
  saveNodes();
297
  }
298
 
299
- // Add a manual node
300
  function addNode() {
301
  const node = createNode(
302
  Math.random() * (stage.width() - 100),
@@ -313,7 +302,6 @@ function addNode() {
313
  saveNodes();
314
  }
315
 
316
- // Update spline connections when nodes move
317
  function updateConnections() {
318
  layer.find('Shape').forEach(shape => {
319
  if (shape.data && shape.data.fromNodeId !== undefined) {
@@ -346,7 +334,6 @@ function updateConnections() {
346
  layer.draw();
347
  }
348
 
349
- // Save nodes and connections to backend
350
  function saveNodes() {
351
  fetch('/save_nodes', {
352
  method: 'POST',
@@ -361,7 +348,8 @@ function saveNodes() {
361
  x: n.data.x,
362
  y: n.data.y,
363
  inputs: n.data.inputs.map(p => p.name),
364
- outputs: n.data.outputs.map(p => p.name)
 
365
  })),
366
  connections: connections
367
  })
@@ -370,5 +358,4 @@ function saveNodes() {
370
  .catch(error => console.error('Error:', error));
371
  }
372
 
373
- // Initial draw
374
  layer.draw();
 
8
  const layer = new Konva.Layer();
9
  stage.add(layer);
10
 
 
11
  let nodes = [];
12
+ let connections = [];
13
  let parsedConnections = [];
14
+ let selectedPort = null;
15
  let disconnectMode = false;
16
 
 
17
  function submitCode() {
18
  const fileInput = document.getElementById('codeFile');
19
  const codeInput = document.getElementById('codeInput').value;
 
39
  return;
40
  }
41
  clearCanvas();
42
+ parsedConnections = data.connections;
43
  createNodesFromParsedData(data.nodes, data.connections);
44
  })
45
  .catch(error => console.error('Error:', error));
46
  }
47
 
 
48
  function clearCanvas() {
49
  nodes.forEach(node => node.destroy());
50
  layer.find('Shape').forEach(shape => shape.destroy());
 
54
  layer.draw();
55
  }
56
 
 
57
  function createNodesFromParsedData(parsedNodes, parsedConnections) {
58
  parsedNodes.forEach(nodeData => {
59
  const node = createNode(
 
63
  nodeData.type,
64
  nodeData.inputs,
65
  nodeData.outputs,
66
+ nodeData.id,
67
+ nodeData.value
68
  );
69
  nodes.push(node);
70
  layer.add(node);
 
73
  saveNodes();
74
  }
75
 
76
+ function createNode(x, y, label, type, inputs = [], outputs = [], id, value = null) {
 
77
  const node = new Konva.Group({
78
  x: x,
79
  y: y,
80
  draggable: true
81
  });
82
 
83
+ // Node appearance based on type
84
+ const isNumberBox = type === 'number_box';
85
+ const color = isNumberBox ? '#ffcccb' : type === 'function' ? '#ffeb3b' : type.includes('variable') ? '#90caf9' : type === 'import' ? '#a5d6a7' : '#ccc';
86
+ const width = isNumberBox ? 80 : 100;
87
+ const height = isNumberBox ? 40 : 50;
88
+
89
  const box = new Konva.Rect({
90
+ width: width,
91
+ height: height,
92
  fill: color,
93
  stroke: 'black',
94
  strokeWidth: 2,
95
  cornerRadius: 5
96
  });
97
 
98
+ // Node label and value
99
+ const textContent = isNumberBox && value ? `${label} = ${value}` : label;
100
  const text = new Konva.Text({
101
+ text: textContent,
102
  fontSize: 12,
103
  fontFamily: 'Arial',
104
  fill: 'black',
105
+ width: width,
106
  align: 'center',
107
+ y: isNumberBox ? 14 : 20
108
  });
109
 
110
  node.add(box);
111
  node.add(text);
112
 
113
+ // Input/output ports
114
  const inputPorts = inputs.map((input, i) => ({
115
  id: `input-${id}-${i}`,
116
  name: input,
 
126
  id: `output-${id}-${i}`,
127
  name: output,
128
  circle: new Konva.Circle({
129
+ x: width,
130
  y: 10 + i * 20,
131
  radius: 5,
132
  fill: 'green'
133
  })
134
  }));
135
 
 
136
  inputPorts.forEach(port => {
137
  node.add(port.circle);
138
  port.circle.on('click', () => {
139
  if (!selectedPort) {
140
  selectedPort = { node, portId: port.id, type: 'input' };
141
+ disconnectMode = true;
142
  } else if (selectedPort.type === 'output' && selectedPort.node !== node) {
 
143
  createSplineConnection(
144
  selectedPort.node,
145
  selectedPort.portId,
 
156
  disconnectMode = false;
157
  saveNodes();
158
  } else if (disconnectMode && selectedPort.type === 'input' && selectedPort.node === node) {
159
+ selectedPort = { node, portId: port.id, type: 'input' };
 
160
  } else {
161
  selectedPort = null;
162
  disconnectMode = false;
 
171
  selectedPort = { node, portId: port.id, type: 'output' };
172
  disconnectMode = false;
173
  } else if (disconnectMode && selectedPort.type === 'input') {
 
174
  const connIndex = connections.findIndex(
175
  c => c.toNodeId === selectedPort.node.data.id &&
176
  c.toPortId === selectedPort.portId &&
 
199
  });
200
  });
201
 
 
202
  node.data = {
203
  id: id,
204
  type: type,
 
206
  inputs: inputPorts,
207
  outputs: outputPorts,
208
  x: x,
209
+ y: y,
210
+ value: value
211
  };
212
 
 
213
  node.on('dragmove', () => {
214
  node.data.x = node.x();
215
  node.data.y = node.y();
 
220
  return node;
221
  }
222
 
 
223
  function createSplineConnection(fromNode, fromPortId, toNode, toPortId) {
224
  const fromPort = fromNode.data.outputs.find(p => p.id === fromPortId);
225
  const toPort = toNode.data.inputs.find(p => p.id === toPortId);
 
230
  const endX = toNode.x() + toPort.circle.x();
231
  const endY = toNode.y() + toPort.circle.y();
232
 
 
233
  const control1X = startX + (endX - startX) / 3;
234
  const control1Y = startY;
235
  const control2X = startX + 2 * (endX - startX) / 3;
 
256
  layer.draw();
257
  }
258
 
 
259
  function autoConnect() {
 
260
  layer.find('Shape').forEach(shape => {
261
  if (shape.data && shape.data.fromNodeId !== undefined) {
262
  shape.destroy();
 
264
  });
265
  connections = [];
266
 
 
267
  parsedConnections.forEach(conn => {
268
  const fromNode = nodes.find(n => n.data.id === conn.from);
269
  const toNode = nodes.find(n => n.data.id === conn.to);
270
  if (fromNode && toNode) {
 
271
  const fromPort = fromNode.data.outputs[0];
272
  const toPort = toNode.data.inputs[0];
273
  if (fromPort && toPort) {
 
286
  saveNodes();
287
  }
288
 
 
289
  function addNode() {
290
  const node = createNode(
291
  Math.random() * (stage.width() - 100),
 
302
  saveNodes();
303
  }
304
 
 
305
  function updateConnections() {
306
  layer.find('Shape').forEach(shape => {
307
  if (shape.data && shape.data.fromNodeId !== undefined) {
 
334
  layer.draw();
335
  }
336
 
 
337
  function saveNodes() {
338
  fetch('/save_nodes', {
339
  method: 'POST',
 
348
  x: n.data.x,
349
  y: n.data.y,
350
  inputs: n.data.inputs.map(p => p.name),
351
+ outputs: n.data.outputs.map(p => p.name),
352
+ value: n.data.value
353
  })),
354
  connections: connections
355
  })
 
358
  .catch(error => console.error('Error:', error));
359
  }
360
 
 
361
  layer.draw();