Spaces:
Running
Running
owenkaplinsky
commited on
Commit
·
f937d73
1
Parent(s):
6eec7f7
Fix ifelse conditions; add tool warnings
Browse files- project/blocks.txt +2 -2
- project/chat.py +32 -8
- project/src/index.js +112 -79
project/blocks.txt
CHANGED
|
@@ -53,8 +53,8 @@ VALUE: lists_sort(inputs(TYPE: "NUMERIC/TEXT/IGNORE_CASE", DIRECTION: "1/-1")) /
|
|
| 53 |
|
| 54 |
# Variables
|
| 55 |
VALUE: variables_get(inputs(VAR: value)) // VAR is a variable ID
|
| 56 |
-
|
| 57 |
-
|
| 58 |
|
| 59 |
# MCP Block inputs
|
| 60 |
VALUE: input_reference_NAME(inputs()) // Replace "NAME" with the name of the MCP block input. Any inputs for the one MCP block can have their blocks made with this. You may ONLY use this if the MCP has an input with this name. If needed you must create inputs and outputs first for the MCP block
|
|
|
|
| 53 |
|
| 54 |
# Variables
|
| 55 |
VALUE: variables_get(inputs(VAR: value)) // VAR is a variable ID
|
| 56 |
+
STATEMENT: variables_set(inputs(VAR: value, VALUE: value)) // VAR is a variable ID
|
| 57 |
+
STATEMENT: math_change(inputs(VAR: value, DELTA: value)) // VAR is a variable ID. To use this, you MUST have used `variables_set` before this
|
| 58 |
|
| 59 |
# MCP Block inputs
|
| 60 |
VALUE: input_reference_NAME(inputs()) // Replace "NAME" with the name of the MCP block input. Any inputs for the one MCP block can have their blocks made with this. You may ONLY use this if the MCP has an input with this name. If needed you must create inputs and outputs first for the MCP block
|
project/chat.py
CHANGED
|
@@ -50,6 +50,9 @@ current_mcp_server_url = None
|
|
| 50 |
deployment_just_happened = False
|
| 51 |
deployment_message = ""
|
| 52 |
|
|
|
|
|
|
|
|
|
|
| 53 |
blocks_context = ""
|
| 54 |
try:
|
| 55 |
file_path = os.path.join(os.path.dirname(__file__), "blocks.txt")
|
|
@@ -893,7 +896,10 @@ def create_gradio_interface():
|
|
| 893 |
|
| 894 |
def chat_with_context(message, history):
|
| 895 |
# Check if API key is set and create/update client
|
| 896 |
-
global client, stored_api_key
|
|
|
|
|
|
|
|
|
|
| 897 |
|
| 898 |
# Use stored key or environment key
|
| 899 |
api_key = stored_api_key or os.environ.get("OPENAI_API_KEY")
|
|
@@ -1081,14 +1087,32 @@ def create_gradio_interface():
|
|
| 1081 |
blockID = function_args.get("blockID", None)
|
| 1082 |
placement_type = function_args.get("type", None)
|
| 1083 |
input_name = function_args.get("input_name", None)
|
| 1084 |
-
|
| 1085 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1086 |
else:
|
| 1087 |
-
|
| 1088 |
-
|
| 1089 |
-
|
| 1090 |
-
|
| 1091 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1092 |
|
| 1093 |
elif function_name == "create_variable":
|
| 1094 |
name = function_args.get("name", "")
|
|
|
|
| 50 |
deployment_just_happened = False
|
| 51 |
deployment_message = ""
|
| 52 |
|
| 53 |
+
# Track if first MCP output block creation attempt has happened in this conversation
|
| 54 |
+
first_output_block_attempted = False
|
| 55 |
+
|
| 56 |
blocks_context = ""
|
| 57 |
try:
|
| 58 |
file_path = os.path.join(os.path.dirname(__file__), "blocks.txt")
|
|
|
|
| 896 |
|
| 897 |
def chat_with_context(message, history):
|
| 898 |
# Check if API key is set and create/update client
|
| 899 |
+
global client, stored_api_key, first_output_block_attempted
|
| 900 |
+
|
| 901 |
+
# Reset output block tracking for this conversation turn
|
| 902 |
+
first_output_block_attempted = False
|
| 903 |
|
| 904 |
# Use stored key or environment key
|
| 905 |
api_key = stored_api_key or os.environ.get("OPENAI_API_KEY")
|
|
|
|
| 1087 |
blockID = function_args.get("blockID", None)
|
| 1088 |
placement_type = function_args.get("type", None)
|
| 1089 |
input_name = function_args.get("input_name", None)
|
| 1090 |
+
|
| 1091 |
+
# Check if this is the first MCP output block creation attempt
|
| 1092 |
+
is_first_output_attempt = (
|
| 1093 |
+
not first_output_block_attempted and
|
| 1094 |
+
placement_type == "input" and
|
| 1095 |
+
input_name and
|
| 1096 |
+
input_name.startswith("R")
|
| 1097 |
+
)
|
| 1098 |
+
|
| 1099 |
+
if is_first_output_attempt:
|
| 1100 |
+
# Mark that we've attempted an output block in this conversation
|
| 1101 |
+
first_output_block_attempted = True
|
| 1102 |
+
# Return warning instead of creating the block
|
| 1103 |
+
tool_result = "[TOOL] Automated warning: Make sure your output block contains the full and entire value needed. Block placement was **not** executed. Retry with the full command needed in one go."
|
| 1104 |
+
result_label = "Output Block Warning"
|
| 1105 |
+
print(Fore.YELLOW + f"[FIRST OUTPUT BLOCK] Intercepted first output block attempt with command `{command}`." + Style.RESET_ALL)
|
| 1106 |
else:
|
| 1107 |
+
# Normal block creation
|
| 1108 |
+
if blockID is None:
|
| 1109 |
+
print(Fore.YELLOW + f"Agent created block with command `{command}`." + Style.RESET_ALL)
|
| 1110 |
+
else:
|
| 1111 |
+
print(Fore.YELLOW + f"Agent created block with command `{command}`, type: {placement_type}, blockID: `{blockID}`." + Style.RESET_ALL)
|
| 1112 |
+
if input_name:
|
| 1113 |
+
print(Fore.YELLOW + f" Input name: {input_name}" + Style.RESET_ALL)
|
| 1114 |
+
tool_result = create_block(command, blockID, placement_type, input_name)
|
| 1115 |
+
result_label = "Create Operation"
|
| 1116 |
|
| 1117 |
elif function_name == "create_variable":
|
| 1118 |
name = function_args.get("name", "")
|
project/src/index.js
CHANGED
|
@@ -528,17 +528,17 @@ const setupUnifiedStream = () => {
|
|
| 528 |
if (validateParenCount > 0) {
|
| 529 |
if (validateParenCount <= 2) {
|
| 530 |
console.log(`[SSE CREATE] Auto-fixing ${validateParenCount} missing closing parentheses in block '${blockType}'`);
|
| 531 |
-
|
| 532 |
// Smart insertion: find the best place to insert closing parentheses
|
| 533 |
// Look for the last comma at depth 0 (which separates key-value pairs)
|
| 534 |
let bestInsertPos = inputsContent.length; // Default to end
|
| 535 |
let depth = 0;
|
| 536 |
let inQuotes = false;
|
| 537 |
let quoteChar = '';
|
| 538 |
-
|
| 539 |
for (let i = inputsContent.length - 1; i >= 0; i--) {
|
| 540 |
const char = inputsContent[i];
|
| 541 |
-
|
| 542 |
// Handle quotes (from right to left)
|
| 543 |
if ((char === '"' || char === "'") && (i === 0 || inputsContent[i - 1] !== '\\')) {
|
| 544 |
if (!inQuotes) {
|
|
@@ -548,20 +548,20 @@ const setupUnifiedStream = () => {
|
|
| 548 |
inQuotes = false;
|
| 549 |
}
|
| 550 |
}
|
| 551 |
-
|
| 552 |
// Handle parentheses (from right to left, so reverse the logic)
|
| 553 |
if (!inQuotes) {
|
| 554 |
if (char === ')') depth++;
|
| 555 |
else if (char === '(') depth--;
|
| 556 |
}
|
| 557 |
-
|
| 558 |
// Found the last comma at depth 0 - this separates the last nested block from following keys
|
| 559 |
if (char === ',' && depth === 0 && !inQuotes) {
|
| 560 |
bestInsertPos = i;
|
| 561 |
break;
|
| 562 |
}
|
| 563 |
}
|
| 564 |
-
|
| 565 |
// Insert the closing parentheses at the best position
|
| 566 |
if (bestInsertPos < inputsContent.length && inputsContent[bestInsertPos] === ',') {
|
| 567 |
// Insert right before the comma
|
|
@@ -683,22 +683,25 @@ const setupUnifiedStream = () => {
|
|
| 683 |
newBlock.pendingAddValues_ = addValues;
|
| 684 |
}
|
| 685 |
} else if (blockType === 'controls_if') {
|
| 686 |
-
// Special handling for if/else blocks
|
| 687 |
-
// IF is required, IFELSEN0, IFELSEN1, etc are optional, ELSE is optional
|
| 688 |
-
// Each condition block goes into IF, IFELSEN0, IFELSEN1, etc inputs
|
| 689 |
-
|
| 690 |
-
// Count how many IF/IFELSEN conditions we have
|
| 691 |
-
let conditionCount = 0;
|
| 692 |
const conditionBlocks = {};
|
|
|
|
| 693 |
let hasElse = false;
|
| 694 |
|
| 695 |
console.log('[SSE CREATE] controls_if inputs:', inputs);
|
| 696 |
|
|
|
|
| 697 |
for (const [key, value] of Object.entries(inputs)) {
|
| 698 |
-
if (key === 'IF') {
|
| 699 |
-
|
| 700 |
-
} else if (key.match(/^IFELSEN\d+$/)) {
|
| 701 |
conditionBlocks[key] = value;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 702 |
} else if (key === 'ELSE' && value === true) {
|
| 703 |
// ELSE is a marker with no value (set to true by parseInputs)
|
| 704 |
console.log('[SSE CREATE] Detected ELSE marker');
|
|
@@ -706,47 +709,24 @@ const setupUnifiedStream = () => {
|
|
| 706 |
}
|
| 707 |
}
|
| 708 |
|
| 709 |
-
|
| 710 |
-
|
| 711 |
-
// Create inputs for each condition
|
| 712 |
-
if (conditionBlocks['IF']) {
|
| 713 |
-
const ifValue = conditionBlocks['IF'];
|
| 714 |
-
if (typeof ifValue === 'string' && ifValue.match(/^\w+\s*\(inputs\(/)) {
|
| 715 |
-
const childBlock = parseAndCreateBlock(ifValue);
|
| 716 |
-
const input = newBlock.getInput('IF0');
|
| 717 |
-
if (input && input.connection && childBlock.outputConnection) {
|
| 718 |
-
childBlock.outputConnection.connect(input.connection);
|
| 719 |
-
}
|
| 720 |
-
}
|
| 721 |
-
}
|
| 722 |
-
|
| 723 |
-
// Handle IFELSEN conditions
|
| 724 |
let elseIfCount = 0;
|
| 725 |
-
for (const
|
| 726 |
-
|
| 727 |
-
|
| 728 |
-
const elseIfValue = value;
|
| 729 |
-
if (typeof elseIfValue === 'string' && elseIfValue.match(/^\w+\s*\(inputs\(/)) {
|
| 730 |
-
// Blockly uses a mutator to add IF/ELSE IF blocks, so we need to configure that
|
| 731 |
-
// For now, just try to create the structure
|
| 732 |
-
const childBlock = parseAndCreateBlock(elseIfValue);
|
| 733 |
-
|
| 734 |
-
// The inputs will be IF1, IF2, etc. for additional conditions
|
| 735 |
-
const ifInputName = 'IF' + (elseIfCount + 1);
|
| 736 |
-
const input = newBlock.getInput(ifInputName);
|
| 737 |
-
if (input && input.connection && childBlock.outputConnection) {
|
| 738 |
-
childBlock.outputConnection.connect(input.connection);
|
| 739 |
-
}
|
| 740 |
-
elseIfCount++;
|
| 741 |
-
}
|
| 742 |
}
|
| 743 |
}
|
| 744 |
|
| 745 |
-
|
|
|
|
|
|
|
|
|
|
| 746 |
newBlock.pendingElseifCount_ = elseIfCount;
|
| 747 |
newBlock.pendingElseCount_ = hasElse ? 1 : 0;
|
| 748 |
-
|
| 749 |
-
//
|
|
|
|
|
|
|
| 750 |
for (const [key, value] of Object.entries(inputs)) {
|
| 751 |
if (typeof value === 'string') {
|
| 752 |
// Check if this is a nested block specification
|
|
@@ -792,44 +772,97 @@ const setupUnifiedStream = () => {
|
|
| 792 |
|
| 793 |
// Initialize the block (renders it)
|
| 794 |
newBlock.initSvg();
|
|
|
|
| 795 |
|
| 796 |
// Apply pending controls_if mutations (must be after initSvg)
|
| 797 |
-
|
| 798 |
-
console.log('[SSE CREATE]
|
| 799 |
-
|
| 800 |
-
|
| 801 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 802 |
|
| 803 |
-
|
| 804 |
-
|
| 805 |
-
|
| 806 |
-
|
| 807 |
-
state.elseIfCount = newBlock.pendingElseifCount_;
|
| 808 |
-
}
|
| 809 |
-
if (newBlock.pendingElseCount_ > 0) {
|
| 810 |
-
state.hasElse = true;
|
| 811 |
}
|
| 812 |
-
console.log('[SSE CREATE] Using loadExtraState with:', state);
|
| 813 |
-
newBlock.loadExtraState(state);
|
| 814 |
-
} else {
|
| 815 |
-
// Fallback: Set the internal state variables and call updateShape_
|
| 816 |
-
newBlock.elseifCount_ = newBlock.pendingElseifCount_;
|
| 817 |
-
newBlock.elseCount_ = newBlock.pendingElseCount_;
|
| 818 |
|
| 819 |
-
|
| 820 |
-
|
| 821 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 822 |
}
|
| 823 |
-
}
|
| 824 |
|
| 825 |
-
|
| 826 |
-
|
| 827 |
-
|
| 828 |
-
|
| 829 |
-
|
| 830 |
-
|
|
|
|
| 831 |
}
|
|
|
|
|
|
|
|
|
|
| 832 |
}
|
|
|
|
|
|
|
| 833 |
}
|
| 834 |
|
| 835 |
// Apply pending text_join mutations (must be after initSvg)
|
|
|
|
| 528 |
if (validateParenCount > 0) {
|
| 529 |
if (validateParenCount <= 2) {
|
| 530 |
console.log(`[SSE CREATE] Auto-fixing ${validateParenCount} missing closing parentheses in block '${blockType}'`);
|
| 531 |
+
|
| 532 |
// Smart insertion: find the best place to insert closing parentheses
|
| 533 |
// Look for the last comma at depth 0 (which separates key-value pairs)
|
| 534 |
let bestInsertPos = inputsContent.length; // Default to end
|
| 535 |
let depth = 0;
|
| 536 |
let inQuotes = false;
|
| 537 |
let quoteChar = '';
|
| 538 |
+
|
| 539 |
for (let i = inputsContent.length - 1; i >= 0; i--) {
|
| 540 |
const char = inputsContent[i];
|
| 541 |
+
|
| 542 |
// Handle quotes (from right to left)
|
| 543 |
if ((char === '"' || char === "'") && (i === 0 || inputsContent[i - 1] !== '\\')) {
|
| 544 |
if (!inQuotes) {
|
|
|
|
| 548 |
inQuotes = false;
|
| 549 |
}
|
| 550 |
}
|
| 551 |
+
|
| 552 |
// Handle parentheses (from right to left, so reverse the logic)
|
| 553 |
if (!inQuotes) {
|
| 554 |
if (char === ')') depth++;
|
| 555 |
else if (char === '(') depth--;
|
| 556 |
}
|
| 557 |
+
|
| 558 |
// Found the last comma at depth 0 - this separates the last nested block from following keys
|
| 559 |
if (char === ',' && depth === 0 && !inQuotes) {
|
| 560 |
bestInsertPos = i;
|
| 561 |
break;
|
| 562 |
}
|
| 563 |
}
|
| 564 |
+
|
| 565 |
// Insert the closing parentheses at the best position
|
| 566 |
if (bestInsertPos < inputsContent.length && inputsContent[bestInsertPos] === ',') {
|
| 567 |
// Insert right before the comma
|
|
|
|
| 683 |
newBlock.pendingAddValues_ = addValues;
|
| 684 |
}
|
| 685 |
} else if (blockType === 'controls_if') {
|
| 686 |
+
// Special handling for if/else blocks - create condition blocks now and store references
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 687 |
const conditionBlocks = {};
|
| 688 |
+
const conditionBlockObjects = {};
|
| 689 |
let hasElse = false;
|
| 690 |
|
| 691 |
console.log('[SSE CREATE] controls_if inputs:', inputs);
|
| 692 |
|
| 693 |
+
// Process condition inputs and store block objects
|
| 694 |
for (const [key, value] of Object.entries(inputs)) {
|
| 695 |
+
if (key === 'IF' || key.match(/^IFELSEN\d+$/)) {
|
| 696 |
+
// This is a condition block specification (not a value for a field)
|
|
|
|
| 697 |
conditionBlocks[key] = value;
|
| 698 |
+
|
| 699 |
+
if (typeof value === 'string' && value.match(/^\w+\s*\(inputs\(/)) {
|
| 700 |
+
// Create the condition block now
|
| 701 |
+
const conditionBlock = parseAndCreateBlock(value);
|
| 702 |
+
conditionBlockObjects[key] = conditionBlock;
|
| 703 |
+
console.log('[SSE CREATE] Created condition block for', key);
|
| 704 |
+
}
|
| 705 |
} else if (key === 'ELSE' && value === true) {
|
| 706 |
// ELSE is a marker with no value (set to true by parseInputs)
|
| 707 |
console.log('[SSE CREATE] Detected ELSE marker');
|
|
|
|
| 709 |
}
|
| 710 |
}
|
| 711 |
|
| 712 |
+
// Count IFELSEN blocks
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 713 |
let elseIfCount = 0;
|
| 714 |
+
for (const key of Object.keys(conditionBlocks)) {
|
| 715 |
+
if (key.match(/^IFELSEN\d+$/)) {
|
| 716 |
+
elseIfCount++;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 717 |
}
|
| 718 |
}
|
| 719 |
|
| 720 |
+
console.log('[SSE CREATE] controls_if parsed: elseIfCount =', elseIfCount, 'hasElse =', hasElse);
|
| 721 |
+
|
| 722 |
+
// Store condition block OBJECTS for later - we'll connect them after mutator creates inputs
|
| 723 |
+
newBlock.pendingConditionBlockObjects_ = conditionBlockObjects;
|
| 724 |
newBlock.pendingElseifCount_ = elseIfCount;
|
| 725 |
newBlock.pendingElseCount_ = hasElse ? 1 : 0;
|
| 726 |
+
console.log('[SSE CREATE] Stored pending condition block objects:', Object.keys(conditionBlockObjects));
|
| 727 |
+
// Skip normal input processing for controls_if - we handle conditions after mutator
|
| 728 |
+
} else if (blockType !== 'controls_if') {
|
| 729 |
+
// Normal block handling (skip for controls_if which is handled specially)
|
| 730 |
for (const [key, value] of Object.entries(inputs)) {
|
| 731 |
if (typeof value === 'string') {
|
| 732 |
// Check if this is a nested block specification
|
|
|
|
| 772 |
|
| 773 |
// Initialize the block (renders it)
|
| 774 |
newBlock.initSvg();
|
| 775 |
+
console.log('[SSE CREATE] === VERSION 2.0 - After initSvg, block type:', newBlock.type);
|
| 776 |
|
| 777 |
// Apply pending controls_if mutations (must be after initSvg)
|
| 778 |
+
try {
|
| 779 |
+
console.log('[SSE CREATE] Checking for controls_if mutations: type =', newBlock.type, 'pendingElseifCount_ =', newBlock.pendingElseifCount_, 'pendingConditionBlockObjects_ =', !!newBlock.pendingConditionBlockObjects_);
|
| 780 |
+
if (newBlock.type === 'controls_if' && (newBlock.pendingElseifCount_ > 0 || newBlock.pendingElseCount_ > 0 || newBlock.pendingConditionBlockObjects_)) {
|
| 781 |
+
console.log('[SSE CREATE] ENTERING controls_if mutation block');
|
| 782 |
+
console.log('[SSE CREATE] Applying controls_if mutation:', {
|
| 783 |
+
elseifCount: newBlock.pendingElseifCount_,
|
| 784 |
+
elseCount: newBlock.pendingElseCount_
|
| 785 |
+
});
|
| 786 |
+
|
| 787 |
+
// Use the loadExtraState method if available (Blockly's preferred way)
|
| 788 |
+
if (typeof newBlock.loadExtraState === 'function') {
|
| 789 |
+
const state = {};
|
| 790 |
+
if (newBlock.pendingElseifCount_ > 0) {
|
| 791 |
+
state.elseIfCount = newBlock.pendingElseifCount_;
|
| 792 |
+
}
|
| 793 |
+
if (newBlock.pendingElseCount_ > 0) {
|
| 794 |
+
state.hasElse = true;
|
| 795 |
+
}
|
| 796 |
+
console.log('[SSE CREATE] Using loadExtraState with:', state);
|
| 797 |
+
newBlock.loadExtraState(state);
|
| 798 |
+
console.log('[SSE CREATE] After loadExtraState');
|
| 799 |
+
} else {
|
| 800 |
+
// Fallback: Set the internal state variables and call updateShape_
|
| 801 |
+
newBlock.elseifCount_ = newBlock.pendingElseifCount_;
|
| 802 |
+
newBlock.elseCount_ = newBlock.pendingElseCount_;
|
| 803 |
|
| 804 |
+
if (typeof newBlock.updateShape_ === 'function') {
|
| 805 |
+
console.log('[SSE CREATE] Calling updateShape_ on controls_if');
|
| 806 |
+
newBlock.updateShape_();
|
| 807 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 808 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 809 |
|
| 810 |
+
// Now that the mutator has created all the inputs, connect the stored condition block objects
|
| 811 |
+
console.log('[SSE CREATE] pendingConditionBlockObjects_ exists?', !!newBlock.pendingConditionBlockObjects_);
|
| 812 |
+
if (newBlock.pendingConditionBlockObjects_) {
|
| 813 |
+
const conditionBlockObjects = newBlock.pendingConditionBlockObjects_;
|
| 814 |
+
console.log('[SSE CREATE] Connecting condition blocks:', Object.keys(conditionBlockObjects));
|
| 815 |
+
|
| 816 |
+
// Connect the IF condition
|
| 817 |
+
if (conditionBlockObjects['IF']) {
|
| 818 |
+
const ifBlock = conditionBlockObjects['IF'];
|
| 819 |
+
const input = newBlock.getInput('IF0');
|
| 820 |
+
console.log('[SSE CREATE] IF0 input exists?', !!input);
|
| 821 |
+
if (input && input.connection && ifBlock.outputConnection) {
|
| 822 |
+
ifBlock.outputConnection.connect(input.connection);
|
| 823 |
+
console.log('[SSE CREATE] Connected IF condition');
|
| 824 |
+
} else {
|
| 825 |
+
console.warn('[SSE CREATE] Could not connect IF - input:', !!input, 'childConnection:', !!ifBlock.outputConnection);
|
| 826 |
+
}
|
| 827 |
+
}
|
| 828 |
+
|
| 829 |
+
// Connect IFELSEN conditions
|
| 830 |
+
console.log('[SSE CREATE] Processing', newBlock.pendingElseifCount_, 'IFELSEN conditions');
|
| 831 |
+
for (let i = 0; i < newBlock.pendingElseifCount_; i++) {
|
| 832 |
+
const key = 'IFELSEN' + i;
|
| 833 |
+
console.log('[SSE CREATE] Looking for key:', key, 'exists?', !!conditionBlockObjects[key]);
|
| 834 |
+
if (conditionBlockObjects[key]) {
|
| 835 |
+
const ifElseBlock = conditionBlockObjects[key];
|
| 836 |
+
// IFELSEN blocks connect to IF1, IF2, etc.
|
| 837 |
+
const inputName = 'IF' + (i + 1);
|
| 838 |
+
const input = newBlock.getInput(inputName);
|
| 839 |
+
console.log('[SSE CREATE] Input', inputName, 'exists?', !!input);
|
| 840 |
+
if (input && input.connection && ifElseBlock.outputConnection) {
|
| 841 |
+
ifElseBlock.outputConnection.connect(input.connection);
|
| 842 |
+
console.log('[SSE CREATE] Connected IFELSEN' + i + ' condition to ' + inputName);
|
| 843 |
+
} else {
|
| 844 |
+
console.warn('[SSE CREATE] Could not connect IFELSEN' + i + ' - input ' + inputName + ' exists:', !!input, 'has connection:', input ? !!input.connection : false, 'childHasOutput:', !!ifElseBlock.outputConnection);
|
| 845 |
+
}
|
| 846 |
+
}
|
| 847 |
+
}
|
| 848 |
+
} else {
|
| 849 |
+
console.warn('[SSE CREATE] No pendingConditionBlockObjects_ found');
|
| 850 |
}
|
|
|
|
| 851 |
|
| 852 |
+
// Verify the ELSE input was created
|
| 853 |
+
if (newBlock.pendingElseCount_ > 0) {
|
| 854 |
+
const elseInput = newBlock.getInput('ELSE');
|
| 855 |
+
console.log('[SSE CREATE] ELSE input after mutation:', elseInput);
|
| 856 |
+
if (!elseInput) {
|
| 857 |
+
console.error('[SSE CREATE] ELSE input was NOT created!');
|
| 858 |
+
}
|
| 859 |
}
|
| 860 |
+
|
| 861 |
+
// Re-render after connecting condition blocks
|
| 862 |
+
newBlock.render();
|
| 863 |
}
|
| 864 |
+
} catch (err) {
|
| 865 |
+
console.error('[SSE CREATE] Error in controls_if mutations:', err);
|
| 866 |
}
|
| 867 |
|
| 868 |
// Apply pending text_join mutations (must be after initSvg)
|