- Install blockly package (~500KB) - Create BlocklyWorkspace React component with serialization - Define custom Noodl blocks (Input/Output, Variables, Objects, Arrays) - Implement JavaScript code generators for all custom blocks - Add theme-aware styling for Blockly workspace - Export initialization functions for easy integration Part of TASK-012: Blockly Visual Logic Integration
8.6 KiB
TASK-008: Investigation Log
Created: 2026-01-11
Status: In Progress
Initial Bug Reports
Reporter: Richard
Date: 2026-01-11
Bug 1: White-on-White Error Tooltips
"The toasts that hover over nodes with errors are white background with white text, so I can't see anything."
Bug 2: Expression/Function Nodes Not Outputting
"The expression nodes and function nodes aren't outputting any data anymore, even when run."
Code Analysis
Bug 1: Tooltip Rendering Path
Flow:
NodeGraphEditorNode.ts- Mouse hover over node with error- Line 608:
PopupLayer.instance.showTooltip()called with error message popuplayer.js- Renders tooltip HTMLpopuplayer.css- Styles the tooltip (LEGACY CSS)
Key Code Location:
// NodeGraphEditorNode.ts:606-615
const health = this.model.getHealth();
if (!health.healthy) {
PopupLayer.instance.showTooltip({
x: evt.pageX,
y: evt.pageY,
position: 'bottom',
content: health.message
});
}
CSS Classes:
.popup-layer-tooltip.popup-layer-tooltip-content.popup-layer-tooltip-arrow
Suspected Issue: Legacy CSS file uses hardcoded colors incompatible with current theme.
Bug 2: Expression Node Analysis
File: packages/noodl-runtime/src/nodes/std-library/expression.js
Execution Flow:
expressioninput changed →set()method called- Calls
this._scheduleEvaluateExpression() - Sets
internal.hasScheduledEvaluation = true - Calls
this.scheduleAfterInputsHaveUpdated(callback) - Callback should:
- Calculate result via
_calculateExpression() - Store in
internal.cachedValue - Call
this.flagOutputDirty('result') - Send signal outputs
- Calculate result via
Output Mechanism:
- Uses getters for outputs (
result,isTrue,isFalse) - Relies on
flagOutputDirty()to trigger downstream updates - Has signal outputs (
isTrueEv,isFalseEv)
Potential Issues:
- Scheduling callback may not fire
flagOutputDirty()may be broken- Context may not be initialized
- Expression compilation may fail silently
Bug 2: Function Node Analysis
File: packages/noodl-runtime/src/nodes/std-library/simplejavascript.js
Execution Flow:
functionScriptinput changed →set()method called- Parses script, calls
this.scheduleRun() - Sets
runScheduled = true - Calls
this.scheduleAfterInputsHaveUpdated(callback) - Callback should:
- Execute async function with
await func.apply(...) - Outputs set via Proxy:
outputs[key] = value - Proxy triggers
flagOutputDirty('out-' + prop)
- Execute async function with
Output Mechanism:
- Uses Proxy to intercept output writes
- Proxy's
settrap callsthis.flagOutputDirty() - Has getters for value outputs
Potential Issues:
- Proxy behavior may have changed
- Scheduling callback may not fire
- Async function errors swallowed
flagOutputDirty()may be broken
Common Patterns
Both nodes rely on:
scheduleAfterInputsHaveUpdated()- scheduling mechanismflagOutputDirty()- output update notification- Getters for output values
If either mechanism is broken, both nodes would fail.
Investigation Steps
Step 1: Verify Scheduling Works ✅
Test: Add console.log to verify callbacks fire
// In Expression node
this.scheduleAfterInputsHaveUpdated(function () {
console.log('🔥 Expression callback FIRED');
// ... rest of code
});
// In Function node
this.scheduleAfterInputsHaveUpdated(() => {
console.log('🔥 Function callback FIRED');
// ... rest of code
});
Expected: Logs should appear when inputs change or Run is triggered.
Step 2: Verify Output Flagging ✅
Test: Add console.log before flagOutputDirty calls
// In Expression node
console.log('🚩 Flagging output dirty: result', internal.cachedValue);
this.flagOutputDirty('result');
// In Function node (Proxy)
console.log('🚩 Flagging output dirty:', 'out-' + prop, value);
this._internal.outputValues[prop] = value;
this.flagOutputDirty('out-' + prop);
Expected: Logs should appear when outputs change.
Step 3: Verify Downstream Updates ✅
Test: Connect a Text node to Expression/Function output, check if it updates
Expected: Text node should show the computed value.
Step 4: Check Console for Errors ✅
Test: Open DevTools console, look for:
- Compilation errors
- Runtime errors
- Promise rejections
- Silent failures
Step 5: Check Context/Scope ✅
Test: Verify this.context and this.context.modelScope exist
console.log('🌍 Context:', this.context);
console.log('🌍 ModelScope:', this.context?.modelScope);
Expected: Should be defined objects, not undefined.
Findings
Tooltip Issue ✅ FIXED
Root Cause: Legacy CSS in popuplayer.css used hardcoded colors:
- Background:
var(--theme-color-secondary)(white in current theme) - Text:
var(--theme-color-fg-highlight)(white) - Result: White text on white background
Fix: Replaced with proper theme tokens:
- Background:
var(--theme-color-bg-3)- dark panel background - Border:
var(--theme-color-border-default)- theme border - Text:
var(--theme-color-fg-default)- readable text color
Status: ✅ Confirmed working by Richard
Node Output Issue ✅ FIXED
Root Cause: JavascriptNodeParser.createNoodlAPI() returns base Noodl API (with Variables, Objects, Arrays) but doesn't include Inputs/Outputs properties. Legacy code using Noodl.Outputs.foo = 'bar' failed with "cannot set properties of undefined".
Function Signature:
function(Inputs, Outputs, Noodl, Component) { ... }
Legacy Code (broken):
Noodl.Outputs.foo = 'bar'; // ❌ Noodl.Outputs is undefined
New Code (worked):
Outputs.foo = 'bar'; // ✅ Direct parameter access
Fix: Augmented Noodl API object in simplejavascript.js:
const noodlAPI = JavascriptNodeParser.createNoodlAPI(this.nodeScope.modelScope);
noodlAPI.Inputs = inputs; // Add reference for backward compatibility
noodlAPI.Outputs = outputs; // Add reference for backward compatibility
Result: Both syntaxes now work:
- ✅
Noodl.Outputs.foo = 'bar'(legacy) - ✅
Outputs.foo = 'bar'(current) - ✅
Noodl.Variables,Noodl.Objects,Noodl.Arrays(unchanged)
Status: ✅ Implemented, ✅ Confirmed working by Richard
Expression Node Issue ✅ FIXED
Root Cause: Expression node compiled functions with function(inputA, inputB, ...) signature, but tried to access Noodl via global scope in function preamble. The global Noodl object wasn't properly initialized or was missing Variables/Objects/Arrays.
Expression: 'text' (string literal) returning 0 instead of "text"
Problem Areas:
-
Function Preamble (lines 296-310): Tries to access global
Noodl:'var NoodlContext = (typeof Noodl !== "undefined") ? Noodl : ...;'; -
Compiled Function (line 273): Only received input parameters, no Noodl:
// Before: function(inputA, inputB, ...) { return (expression); }
Fix: Pass Noodl API as parameter to compiled functions:
-
In
_compileFunction()(lines 270-272):// Add 'Noodl' as last parameter for backward compatibility args.push('Noodl'); -
In
_calculateExpression()(lines 250-257):// Get proper Noodl API and append as last parameter const JavascriptNodeParser = require('../../javascriptnodeparser'); const noodlAPI = JavascriptNodeParser.createNoodlAPI(this.context && this.context.modelScope); const argsWithNoodl = internal.inputValues.concat([noodlAPI]); return internal.compiledFunction.apply(null, argsWithNoodl);
Result:
- ✅
'text'should return "text" (string) - ✅
123should return 123 (number) - ✅
Variables.myVarshould access Noodl Variables - ✅
Objects.myObjshould access Noodl Objects - ✅ All math functions still work (min, max, cos, sin, etc.)
Status: ✅ Implemented, awaiting testing confirmation
Timeline
- 2026-01-11 10:40 - Task created, initial investigation started
- Entries to be added as investigation progresses
Related Issues
- May be related to React 19 migration (Phase 1)
- May be related to runtime changes (Phase 2)
- Similar issues may exist in other node types
Next Steps
- Add debug logging to both node types
- Test in running editor
- Reproduce bugs with minimal test case
- Identify exact failure point
- Implement fixes
- Document in LEARNINGS.md