Skip to content

Commit 2e43a77

Browse files
committed
Added type support for Switch node cases (string/number/boolean) and improved variable handling
1 parent d9fecd0 commit 2e43a77

File tree

7 files changed

+153
-47
lines changed

7 files changed

+153
-47
lines changed

src/CodeGenerator.js

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ class CodeGenerator {
2323
this.addLine();
2424
}
2525
this.generateImports();
26-
this.generateVariableDeclarations();
2726
this.generateMainFunction();
2827
return this.code.trim(); // Trim to remove any trailing newlines
2928
}
@@ -33,27 +32,6 @@ class CodeGenerator {
3332
this.addLine();
3433
}
3534

36-
generateVariableDeclarations() {
37-
const declaredVariables = new Set();
38-
this.nodes.forEach(node => {
39-
if (node.type === 'Variable') {
40-
const { name, type, initialValue } = node.properties;
41-
if (name && type && !declaredVariables.has(name)) {
42-
let declaration = `${this.settings.useConst ? 'const' : 'let'} ${name}`;
43-
if (initialValue !== undefined && initialValue !== '') {
44-
declaration += ` = ${this.getTypedValue(type, initialValue)}`;
45-
}
46-
this.addLine(declaration);
47-
this.variables.set(name, type);
48-
declaredVariables.add(name);
49-
}
50-
}
51-
});
52-
if (declaredVariables.size > 0) {
53-
this.addLine();
54-
}
55-
}
56-
5735
generateMainFunction() {
5836
this.hasAsyncOperations = this.nodes.some(node => node.type === 'HttpRequest' || node.type === 'WaitForSeconds');
5937
const asyncKeyword = this.hasAsyncOperations ? 'async ' : '';
@@ -202,8 +180,21 @@ class CodeGenerator {
202180

203181
// Generate case statements
204182
cases.forEach((caseObj, index) => {
205-
const caseValue = ignoreCase ? caseObj.value.toLowerCase() : caseObj.value;
206-
this.addLine(`case ${JSON.stringify(caseValue)}:`);
183+
// Convert the case value based on its type
184+
let caseValue;
185+
switch (caseObj.type) {
186+
case 'number':
187+
caseValue = Number(caseObj.value);
188+
break;
189+
case 'boolean':
190+
caseValue = caseObj.value === 'true';
191+
break;
192+
default: // string
193+
caseValue = ignoreCase ? caseObj.value.toLowerCase() : caseObj.value;
194+
caseValue = JSON.stringify(caseValue);
195+
}
196+
197+
this.addLine(`case ${caseValue}:`);
207198
this.indentLevel++;
208199

209200
// Find and generate code for the case branch
@@ -282,11 +273,24 @@ class CodeGenerator {
282273
}
283274

284275
handleVariableNode(node) {
285-
const { name } = node.properties;
276+
const { name, type, initialValue } = node.properties;
286277
const inputValue = this.getNodeInputValue(node, 1);
287-
if (inputValue !== undefined && inputValue !== name) {
288-
this.addLine(`${name} = ${inputValue};`);
278+
279+
if (!this.variables.has(name)) {
280+
// First time this variable is used - declare and initialize it
281+
let declaration = `${this.settings.useConst ? 'const' : 'let'} ${name}`;
282+
if (inputValue !== undefined) {
283+
declaration += ` = ${inputValue}`;
284+
} else if (initialValue !== undefined && initialValue !== '') {
285+
declaration += ` = ${this.getTypedValue(type, initialValue)}`;
286+
}
287+
this.addLine(declaration);
288+
this.variables.set(name, type);
289+
} else if (inputValue !== undefined && inputValue !== name) {
290+
// Variable already declared, just update its value
291+
this.addLine(`${name} = ${inputValue}`);
289292
}
293+
290294
this.nodeOutputs.set(node.id, name);
291295
}
292296

@@ -435,7 +439,8 @@ class CodeGenerator {
435439
} else {
436440
const indentation = ' '.repeat(this.indentLevel);
437441
const semicolon = this.settings.useSemicolons && this.shouldAddSemicolon(line) ? ';' : '';
438-
this.code += `${indentation}${line}${semicolon}\n`;
442+
const cleanLine = line.replace(/;$/, '');
443+
this.code += `${indentation}${cleanLine}${semicolon}\n`;
439444
}
440445
}
441446

@@ -692,8 +697,12 @@ class CodeGenerator {
692697
/^try/,
693698
/^catch/,
694699
/^finally/,
700+
/^case\s/, // Added to prevent semicolons after case statements
701+
/^default:/, // Added to prevent semicolons after default:
695702
/\{$/,
696703
/\}$/,
704+
/\};$/, // Added to prevent double semicolons
705+
/;$/, // Added to prevent double semicolons
697706
// Also don't add semicolons after comments
698707
/^\/\//,
699708
/^\/\*/

src/VisualScripting.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,30 @@ const VisualScripting = () => {
356356
break;
357357
case 'loadExample':
358358
if (examples[param]) {
359+
// Find any Switch nodes and update nodeTypes with dynamic outputs
360+
examples[param].nodes.forEach(node => {
361+
if (node.type === 'Switch' && node.properties.cases) {
362+
const switchNode = nodeTypes['Switch'];
363+
// Create a copy of the original outputs
364+
const baseOutputs = [...switchNode.outputs];
365+
366+
// Add case outputs dynamically
367+
node.properties.cases.forEach((caseItem, index) => {
368+
baseOutputs.splice(index, 0, {
369+
type: 'control',
370+
name: caseItem.output,
371+
description: `Triggered when value matches ${caseItem.value}`
372+
});
373+
});
374+
375+
// Create a temporary node type with the dynamic outputs
376+
nodeTypes['Switch'] = {
377+
...switchNode,
378+
outputs: baseOutputs
379+
};
380+
}
381+
});
382+
359383
setNodes(examples[param].nodes);
360384
setEdges(examples[param].edges);
361385
setUndoStack([]);

src/components/GraphInspector.js

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,23 +67,56 @@ const GraphInspector = ({
6767
<div className={styles.casesContainer}>
6868
{(node.properties.cases || []).map((caseObj, index) => (
6969
<div key={index} className={styles.caseRow}>
70-
<input
71-
type="text"
72-
value={caseObj.value}
70+
<select
71+
value={caseObj.type || 'string'}
7372
onChange={(e) => {
7473
const newCases = [...node.properties.cases];
75-
newCases[index] = { ...caseObj, value: e.target.value };
74+
newCases[index] = {
75+
...caseObj,
76+
type: e.target.value,
77+
// Convert value to match new type
78+
value: e.target.value === 'number' ?
79+
(isNaN(caseObj.value) ? '0' : caseObj.value) :
80+
String(caseObj.value)
81+
};
7682
updateNodeProperty('cases', newCases);
7783
}}
78-
placeholder="Case value"
79-
className={`${styles.input} ${config.isDarkTheme ? styles.inputDark : styles.inputLight}`}
80-
/>
84+
className={`${styles.typeSelect} ${config.isDarkTheme ? styles.inputDark : styles.inputLight}`}
85+
>
86+
<option value="string">String</option>
87+
<option value="number">Number</option>
88+
<option value="boolean">Boolean</option>
89+
</select>
90+
{caseObj.type === 'boolean' ? (
91+
<select
92+
value={caseObj.value}
93+
onChange={(e) => {
94+
const newCases = [...node.properties.cases];
95+
newCases[index] = { ...caseObj, value: e.target.value };
96+
updateNodeProperty('cases', newCases);
97+
}}
98+
className={`${styles.input} ${config.isDarkTheme ? styles.inputDark : styles.inputLight}`}
99+
>
100+
<option value="true">true</option>
101+
<option value="false">false</option>
102+
</select>
103+
) : (
104+
<input
105+
type={caseObj.type === 'number' ? 'number' : 'text'}
106+
value={caseObj.value}
107+
onChange={(e) => {
108+
const newCases = [...node.properties.cases];
109+
newCases[index] = { ...caseObj, value: e.target.value };
110+
updateNodeProperty('cases', newCases);
111+
}}
112+
placeholder="Case value"
113+
className={`${styles.input} ${config.isDarkTheme ? styles.inputDark : styles.inputLight}`}
114+
/>
115+
)}
81116
<button
82117
onClick={() => {
83118
const newCases = node.properties.cases.filter((_, i) => i !== index);
84119
updateNodeProperty('cases', newCases);
85-
86-
// Remove the corresponding output
87120
nodeType.outputs = nodeType.outputs.filter((_, i) => i !== index + 1);
88121
}}
89122
className={`${styles.removeButton} ${config.isDarkTheme ? styles.removeButtonDark : styles.removeButtonLight}`}
@@ -96,11 +129,13 @@ const GraphInspector = ({
96129
onClick={() => {
97130
const newCases = [
98131
...(node.properties.cases || []),
99-
{ value: '', output: `Case ${(node.properties.cases || []).length + 1}` }
132+
{
133+
value: '',
134+
type: 'string',
135+
output: `Case ${(node.properties.cases || []).length + 1}`
136+
}
100137
];
101138
updateNodeProperty('cases', newCases);
102-
103-
// Add new output
104139
nodeType.outputs.push({
105140
type: 'control',
106141
name: `Case ${node.properties.cases.length + 1}`,

src/components/GraphInspector.module.css

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -282,15 +282,31 @@
282282
.caseRow {
283283
display: flex;
284284
gap: 8px;
285+
margin-bottom: 8px;
285286
align-items: center;
286-
width: 100%;
287+
}
288+
289+
.typeSelect {
290+
width: 100px;
291+
flex-shrink: 0;
292+
padding: 8px;
293+
border-radius: 4px;
294+
border: 1px solid;
295+
font-family: 'Inter', sans-serif;
296+
font-size: 14px;
287297
box-sizing: border-box;
288298
}
289299

290-
.caseRow input {
291-
flex: 1;
292-
min-width: 150px;
293-
width: calc(100% - 40px);
300+
.typeSelect.inputDark {
301+
background: #1e1e1e;
302+
border-color: #444;
303+
color: white;
304+
}
305+
306+
.typeSelect.inputLight {
307+
background: white;
308+
border-color: #ddd;
309+
color: black;
294310
}
295311

296312
.addButton, .removeButton {

src/components/MenuBar.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ const MenuBar = ({
7272
<button key="example1" onClick={() => handleMenuItemClick('loadExample', 'example1')} className={`${styles.menuItemButton} ${styles[theme]}`}>Example 1: Hello World</button>,
7373
<button key="example2" onClick={() => handleMenuItemClick('loadExample', 'example2')} className={`${styles.menuItemButton} ${styles[theme]}`}>Example 2: Basic Math</button>,
7474
<button key="example3" onClick={() => handleMenuItemClick('loadExample', 'example3')} className={`${styles.menuItemButton} ${styles[theme]}`}>Example 3: If Statement</button>,
75-
<button key="example4" onClick={() => handleMenuItemClick('loadExample', 'example4')} className={`${styles.menuItemButton} ${styles[theme]}`}>Example 4: Random Number</button>
75+
<button key="example4" onClick={() => handleMenuItemClick('loadExample', 'example4')} className={`${styles.menuItemButton} ${styles[theme]}`}>Example 4: Random Number</button>,
76+
<button key="example5" onClick={() => handleMenuItemClick('loadExample', 'example5')} className={`${styles.menuItemButton} ${styles[theme]}`}>Example 5: Switch Statement</button>
7677
]}
7778
</div>
7879
)}

src/examples.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,27 @@ const examples = {
5555
{ start: { nodeId: 2, isInput: false, index: 0 }, end: { nodeId: 3, isInput: true, index: 0 } },
5656
{ start: { nodeId: 3, isInput: false, index: 1 }, end: { nodeId: 4, isInput: true, index: 0 } },
5757
]
58+
},
59+
example5: {
60+
nodes: [
61+
{ id: 1, type: 'OnStart', x: 100, y: 100, properties: {} },
62+
{ id: 2, type: 'Variable', x: 350, y: 100, properties: { name: 'a', type: 'number', initialValue: '5' } },
63+
{ id: 4, type: 'Switch', x: 600, y: 100, properties: { cases: [
64+
{ value: '5', output: 'Case 1' },
65+
{ value: '3', output: 'Case 2' }
66+
], ignoreCase: false } },
67+
{ id: 5, type: 'Log', x: 850, y: 25, properties: { message: 'Value is 5', logType: 'log' } },
68+
{ id: 6, type: 'Log', x: 850, y: 100, properties: { message: 'Value is 3', logType: 'log' } },
69+
{ id: 7, type: 'Log', x: 850, y: 175, properties: { message: 'No match', logType: 'log' } }
70+
],
71+
edges: [
72+
{ start: { nodeId: 1, isInput: false, index: 0 }, end: { nodeId: 2, isInput: true, index: 0 } },
73+
{ start: { nodeId: 2, isInput: false, index: 0 }, end: { nodeId: 4, isInput: true, index: 0 } },
74+
{ start: { nodeId: 2, isInput: false, index: 1 }, end: { nodeId: 4, isInput: true, index: 1 } },
75+
{ start: { nodeId: 4, isInput: false, index: 0 }, end: { nodeId: 5, isInput: true, index: 0 } },
76+
{ start: { nodeId: 4, isInput: false, index: 1 }, end: { nodeId: 6, isInput: true, index: 0 } },
77+
{ start: { nodeId: 4, isInput: false, index: 2 }, end: { nodeId: 7, isInput: true, index: 0 } }
78+
]
5879
}
5980
};
6081

src/nodeDefinitions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ export const nodeTypes = {
324324
name: 'cases',
325325
type: 'array',
326326
default: [
327-
{ value: '', output: 'Case 1' }
327+
{ value: '', type: 'string', output: 'Case 1' }
328328
],
329329
visible: true
330330
},

0 commit comments

Comments
 (0)