- 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
TASK-012: Blockly Visual Logic Integration
Metadata
| Field | Value |
|---|---|
| ID | TASK-012 |
| Phase | Phase 3 (Editor UX Overhaul) |
| Priority | 🟠 High |
| Difficulty | 🔴 Hard |
| Estimated Time | 4-6 weeks |
| Prerequisites | TASK-006 (Expression Overhaul) recommended but not blocking |
| Branch | task/012-blockly-logic-builder |
Objective
Integrate Google Blockly into Nodegx to provide visual block-based programming as a bridge between nocode nodes and JavaScript, enabling users to build complex logic without writing code.
Background
The "JavaScript Cliff" Problem
Nodegx inherits Noodl's powerful but intimidating transition from visual nodes to code:
NoCode Zone JS Zone
───────────── ────────
Visual nodes ─────[CLIFF]─────► Expression/Function nodes
Condition node Noodl.Variables.isLoggedIn ? x : y
Boolean node Inputs.a + Inputs.b
String Format Outputs.result = computation
Current observations from coaching Noodl users:
- The built-in nocode nodes become limited quickly
- Teaching customization often requires saying "actually an expression would be better here"
- Most people resist dipping into JavaScript - it's a significant turnoff
- The original creators imagined users would be tempted into JS gradually, but this rarely happens
The Blockly Solution
Blockly provides visual block-based programming that:
- Eliminates syntax anxiety (no semicolons, parentheses, typos)
- Makes logic tangible and manipulable
- Generates real JavaScript that curious users can inspect
- Has proven success (Scratch, Code.org, MakeCode, MIT App Inventor)
This is similar to our JSON editor approach: visual nocode option available, with code view for the curious.
Why Blockly?
Research confirms Blockly is the right choice:
- Industry standard: Powers Scratch 3.0, Code.org, Microsoft MakeCode, MIT App Inventor
- Active development: Transitioned to Raspberry Pi Foundation (November 2025) ensuring education-focused stewardship
- Mature library: 13+ years of development, extensive documentation
- Embeddable: 100% client-side, ~500KB, no server dependencies
- Customizable: Full control over toolbox, blocks, and code generation
- No real alternatives: Other "alternatives" are either built on Blockly or complete platforms (not embeddable libraries)
Current State
Existing Code Nodes
| Node | Purpose | Limitation |
|---|---|---|
| Expression | Single expression evaluation | Requires JS syntax knowledge |
| Function | Multi-line JavaScript | Full JS required |
| Script | External script loading | Advanced use case |
User Pain Points
- Backend integration barrier: "How do I hook up my backend?" often requires Function nodes
- Conditional logic complexity: Even simple if/else requires Expression node JS
- Data transformation: Mapping/filtering arrays requires JS knowledge
- No gradual learning path: Jump from visual to text is too steep
Desired State
Two new node types that provide visual block-based logic:
1. Logic Builder Node
Full-featured Blockly workspace for complex, event-driven logic:
┌─────────────────────────────────────┐
│ Logic Builder: "ProcessOrder" │
│ │
│ ○ orderData result ○ │
│ ○ userInfo error ○ │
│ ⚡ process ⚡ success │
│ ⚡ failure │
│ │
│ [Edit Logic Blocks] │
└─────────────────────────────────────┘
- Multiple inputs and outputs (data and signals)
- Event-driven logic (when signal triggered, do X)
- Full Noodl API access (Variables, Objects, Arrays, Records)
- Tabbed editing experience in node canvas
2. Expression Builder Node
Simplified Blockly for single-value expressions:
┌───────────────────────────────────────────┐
│ Expression Builder │
├───────────────────────────────────────────┤
│ ○ price result ○ │
│ ○ quantity │
│ ○ discount │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ [price] × [quantity] × (1 - [disc]) │ │
│ └─────────────────────────────────────┘ │
└───────────────────────────────────────────┘
- Single result output
- Inline or small modal editor
- Perfect for computed values, conditionals, formatting
Node Naming Distinction
To help users choose the right node:
| Node | Mental Model | Subtitle/Description | Icon |
|---|---|---|---|
| Logic Builder | "Do things when stuff happens" | "Build event-driven logic visually" | ⚡ or flowchart |
| Expression Builder | "Calculate something" | "Combine values visually" | f(x) or calculator |
Existing Node Renaming
For clarity, rename existing code nodes:
| Current Name | New Name |
|---|---|
| Expression | JavaScript Expression |
| Function | JavaScript Function |
| Script | JavaScript Script |
Scope
In Scope
- Logic Builder node with full Blockly workspace
- Expression Builder node with simplified Blockly
- Tabbed canvas system for Logic Builder editing
- Custom Noodl block categories (Variables, Objects, Arrays, I/O)
- Auto-detection of inputs/outputs from blocks
- I/O summary panel
- Hidden "View Code" button (read-only JS output)
- Blockly workspace persistence as node parameter
- Rename existing Expression/Function/Script to "JavaScript X"
Out of Scope (Future Phases)
- Records/BYOB blocks (requires Phase 5 BYOB completion)
- Navigation blocks
- Users/Auth blocks
- Cloud Functions blocks
- AI-assisted block suggestions
- Block-to-code learning mode
Technical Approach
Architecture Overview
┌─────────────────────────────────────────────────────────────────────┐
│ LOGIC BUILDER NODE │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ Blockly Workspace │ │
│ │ (Custom toolbox with Noodl categories) │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ Code Generator │ │
│ │ Blockly → JavaScript with Noodl context │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────┐ │ ┌─────────────────────────┐ │
│ │ I/O Detector │◄──────┴───────►│ Generated JS (hidden) │ │
│ │ (auto-ports) │ │ [View Code] button │ │
│ └──────────────────┘ └─────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ Node Port Registration │ │
│ │ Dynamic inputs/outputs based on detected blocks │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Tabbed Canvas System
When opening a Logic Builder node for editing:
┌─────────────────────────────────────────────────────────────────────┐
│ [Canvas] [ProcessOrder ×] [ValidateUser ×] │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Blockly Workspace │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ when process │ │ set result │ │ │
│ │ │ is triggered │────►│ to [value] │ │ │
│ │ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ I/O Summary ─────────────────┐ ┌─ View Code (read-only) ──┐ │
│ │ Inputs: orderData, userInfo │ │ function execute() { │ │
│ │ Outputs: result, error │ │ if (Inputs.orderData) │ │
│ │ Signals: process → success │ │ ... │ │
│ └───────────────────────────────┘ └──────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
Tab behavior:
- Clicking "Edit Logic Blocks" opens a new tab named after the node
- Canvas tab always available to flip back
- Tabs reset when leaving component view
- Multiple Logic Builder nodes can be open simultaneously
Custom Blockly Blocks - Tier 1 (This Task)
INPUTS/OUTPUTS
├── 📥 Get Input [name ▼]
├── 📥 Define Input [name] type [type ▼]
├── 📤 Set Output [name ▼] to [value]
├── 📤 Define Output [name] type [type ▼]
└── ⚡ Send Signal [name ▼]
└── ⚡ Define Signal Input [name]
└── ⚡ Define Signal Output [name]
VARIABLES (Noodl.Variables)
├── 📖 Get Variable [name]
├── ✏️ Set Variable [name] to [value]
└── 👁️ When Variable [name] changes
OBJECTS (Noodl.Objects / Noodl.Model)
├── 📖 Get Object [id]
├── 📖 Get Object [id] property [prop]
├── ✏️ Set Object [id] property [prop] to [value]
├── ➕ Create Object with ID [id]
└── 👁️ When Object [id] changes
ARRAYS (Noodl.Arrays / Noodl.Collection)
├── 📋 Get Array [name]
├── ➕ Add [item] to Array [name]
├── ➖ Remove [item] from Array [name]
├── 🔢 Array [name] length
├── 🔄 For each [item] in Array [name]
└── 👁️ When Array [name] changes
LOGIC (Standard Blockly)
├── if / else if / else
├── comparison (=, ≠, <, >, ≤, ≥)
├── boolean (and, or, not)
├── loops (repeat, while, for each)
└── math operations
TEXT (Standard Blockly)
├── text join
├── text length
├── text contains
└── text substring
EVENTS
├── ⚡ When [signal input ▼] is triggered
└── ⚡ Then send [signal output ▼]
Future Block Categories (Post-BYOB)
RECORDS (Phase 5+ after BYOB)
├── 🔍 Query [collection] where [filter]
├── ➕ Create Record in [collection]
├── ✏️ Update Record [id] in [collection]
├── 🗑️ Delete Record [id]
└── 🔢 Count Records in [collection]
NAVIGATION (Future)
├── 🧭 Navigate to [page]
├── 🔗 Navigate to path [/path]
└── 📦 Show Popup [component]
CONFIG (When Config node complete)
├── ⚙️ Get Config [key]
└── 🔒 Get Secret [key]
Key Files to Modify
| File | Changes |
|---|---|
packages/noodl-editor/package.json |
Add blockly dependency |
packages/noodl-runtime/src/nodelibraryexport.js |
Register new nodes |
packages/noodl-runtime/src/nodes/std-library/expression.js |
Rename to "JavaScript Expression" |
packages/noodl-runtime/src/nodes/std-library/javascriptfunction.js |
Rename to "JavaScript Function" |
New Files to Create
| File | Purpose |
|---|---|
packages/noodl-editor/src/editor/src/views/BlocklyEditor/ |
Blockly workspace React component |
packages/noodl-editor/src/editor/src/views/BlocklyEditor/BlocklyWorkspace.tsx |
Main workspace component |
packages/noodl-editor/src/editor/src/views/BlocklyEditor/NoodlBlocks.ts |
Custom block definitions |
packages/noodl-editor/src/editor/src/views/BlocklyEditor/NoodlGenerators.ts |
JavaScript code generators |
packages/noodl-editor/src/editor/src/views/BlocklyEditor/BlocklyToolbox.ts |
Toolbox configuration |
packages/noodl-editor/src/editor/src/views/BlocklyEditor/IODetector.ts |
Auto-detect I/O from blocks |
packages/noodl-runtime/src/nodes/std-library/logic-builder.js |
Logic Builder node definition |
packages/noodl-runtime/src/nodes/std-library/expression-builder.js |
Expression Builder node definition |
packages/noodl-editor/src/editor/src/views/nodegrapheditor/CanvasTabs.tsx |
Tab system for canvas |
Dependencies
blocklynpm package (~500KB)- No server-side dependencies
Implementation Plan
Phase A: Foundation (Week 1)
-
Install and configure Blockly
- Add to package.json
- Create basic React wrapper component
- Verify rendering in editor
-
Create basic custom blocks
- Input/Output blocks
- Variable get/set blocks
- Verify code generation
-
Storage mechanism
- Serialize workspace to JSON
- Store as node parameter
- Load/restore workspace
Phase B: Logic Builder Node (Week 2)
-
Node definition
- Runtime node structure
- Dynamic port registration
- Code execution from generated JS
-
I/O auto-detection
- Parse workspace for Input/Output blocks
- Update node ports dynamically
- I/O summary panel
-
Editor integration
- Modal editor (initial implementation)
- "Edit Logic Blocks" button in properties
Phase C: Tabbed Canvas System (Week 3)
-
Tab infrastructure
- CanvasTabs component
- Tab state management
- Component view scope
-
Tab behavior
- Open/close tabs
- Tab naming from node
- Reset on component change
-
Polish
- Tab switching animation
- Unsaved indicator
- Keyboard shortcuts
Phase D: Expression Builder Node (Week 4)
-
Simplified workspace
- Limited toolbox (no events/signals)
- Single result output
- Inline or small modal
-
Node definition
- Single output port
- Expression evaluation
- Type inference
Phase E: Full Block Library & Polish (Weeks 5-6)
-
Complete Tier 1 blocks
- Objects blocks with property access
- Arrays blocks with iteration
- Event/signal blocks
-
Code viewer
- "View Code" button
- Read-only JS display
- Syntax highlighting
-
Rename existing nodes
- Expression → JavaScript Expression
- Function → JavaScript Function
- Script → JavaScript Script
-
Testing & documentation
- Unit tests for code generation
- Integration tests for node behavior
- User documentation
Testing Plan
Unit Tests
- Block definitions load correctly
- Code generator produces valid JavaScript
- I/O detector finds all Input/Output blocks
- Workspace serialization round-trips correctly
Integration Tests
- Logic Builder node executes generated code
- Signal inputs trigger execution
- Outputs update connected nodes
- Variables/Objects/Arrays access works
Manual Testing
- Create Logic Builder with simple if/else logic
- Connect inputs/outputs to other nodes
- Verify signal flow works
- Test workspace persistence (save/reload project)
- Test tab system navigation
- Verify "View Code" shows correct JS
- Test Expression Builder for computed values
- Performance test with complex block arrangements
Success Criteria
- Logic Builder node fully functional with Blockly workspace
- Expression Builder node for simple expressions
- Auto-detection of I/O from blocks works reliably
- Tabbed canvas system for editing multiple Logic Builders
- All Tier 1 blocks implemented and working
- "View Code" button shows generated JavaScript (read-only)
- Existing code nodes renamed to "JavaScript X"
- No performance regression in editor
- Works in both editor preview and deployed apps
Risks & Mitigations
| Risk | Mitigation |
|---|---|
| Blockly bundle size (~500KB) | Lazy-load only when Logic Builder opened |
| Blockly styling conflicts | Scope styles carefully, use shadow DOM if needed |
| Generated code security | Same sandbox as Function node, no new risks |
| Tab system complexity | Start with modal, upgrade to tabs if feasible |
| I/O detection edge cases | Require explicit "Define Input/Output" blocks for ports |
Rollback Plan
All changes are additive:
- New nodes can be removed without breaking existing projects
- Blockly dependency can be removed
- Tab system is independent of node functionality
- Renamed nodes can have aliases for backward compatibility
Future Enhancements (Separate Tasks)
- Records blocks - After BYOB (Phase 5)
- Navigation blocks - Page/popup navigation
- AI-assisted blocks - "Describe what you want" → generates blocks
- Block templates - Common patterns as reusable block groups
- Debugging - Step-through execution, breakpoints
- Learning mode - Side-by-side blocks and generated code
References
- Google Blockly Documentation
- Blockly GitHub Repository
- Blockly Samples (plugins, examples)
- MIT App Inventor Blocks - Reference for event-driven block patterns
- Backendless Blockly - Richard's reference for block-based backend logic
- TASK-006: Expression Overhaul (related enhancement)
- Phase 5 BYOB: For future Records blocks integration