Files
OpenNoodl/dev-docs/tasks/phase-3-editor-ux-overhaul/TASK-012-blockly-integration/DRAG-DROP-FIX-ATTEMPT.md
2026-01-12 13:23:12 +01:00

5.8 KiB

Blockly Drag-and-Drop Fix Attempt

Date: 2026-01-11
Status: Fix Implemented - Testing Required
Severity: High (Core functionality)

Problem Summary

Two critical issues with Blockly integration:

  1. Drag Timeout: Blocks could only be dragged for ~1000ms before gesture terminated
  2. Connection Errors: Console flooded with errors when trying to connect blocks

Root Cause Analysis

The original implementation used blanket debouncing on ALL Blockly change events:

// ❌ OLD APPROACH - Debounced ALL events
const changeListener = () => {
  if (changeTimeoutRef.current) clearTimeout(changeTimeoutRef.current);

  changeTimeoutRef.current = setTimeout(() => {
    const json = JSON.stringify(Blockly.serialization.workspaces.save(workspace));
    const code = javascriptGenerator.workspaceToCode(workspace);
    onChange(workspace, json, code);
  }, 150);
};

Why This Caused Problems

  1. During drag operations: Blockly fires MANY events (BLOCK_DRAG, BLOCK_MOVE, etc.)
  2. Each event triggered: A new debounce timeout
  3. React state updates: Potentially caused re-renders during gesture
  4. Blockly's internal state: Expected immediate consistency, but our debounce + React async updates created race conditions
  5. Insertion markers: When trying to show connection previews, Blockly tried to update blocks that were in an inconsistent state

The Solution

Event Filtering - Only respond to events that actually change workspace structure:

// ✅ NEW APPROACH - Filter events intelligently
const changeListener = (event: Blockly.Events.Abstract) => {
  if (!onChange || !workspace) return;

  // Ignore UI events that don't change workspace structure
  if (event.type === Blockly.Events.BLOCK_DRAG) return;
  if (event.type === Blockly.Events.BLOCK_MOVE && !event.isUiEvent) return;
  if (event.type === Blockly.Events.SELECTED) return;
  if (event.type === Blockly.Events.CLICK) return;
  if (event.type === Blockly.Events.VIEWPORT_CHANGE) return;
  if (event.type === Blockly.Events.TOOLBOX_ITEM_SELECT) return;
  if (event.type === Blockly.Events.THEME_CHANGE) return;
  if (event.type === Blockly.Events.TRASHCAN_OPEN) return;

  // For UI events that DO change workspace, debounce them
  const isUiEvent = event.isUiEvent;

  if (isUiEvent) {
    // Debounce user-initiated changes (300ms)
    changeTimeoutRef.current = setTimeout(() => {
      const json = JSON.stringify(Blockly.serialization.workspaces.save(workspace));
      const code = javascriptGenerator.workspaceToCode(workspace);
      onChange(workspace, json, code);
    }, 300);
  } else {
    // Programmatic changes fire immediately (undo/redo, loading)
    const json = JSON.stringify(Blockly.serialization.workspaces.save(workspace));
    const code = javascriptGenerator.workspaceToCode(workspace);
    onChange(workspace, json, code);
  }
};

Key Changes

  1. Event type checking: Ignore events that are purely UI feedback
  2. UI vs Programmatic: Different handling based on event source
  3. No interference with gestures: BLOCK_DRAG events are completely ignored
  4. Longer debounce: Increased from 150ms to 300ms for stability
  5. Immediate programmatic updates: Undo/redo and loading don't debounce

Expected Results

Before Fix

  • Drag stops after ~1000ms
  • Console errors during connection attempts
  • Insertion markers cause state corruption
  • But: no event spam (previous fix still working)

After Fix

  • Drag continuously for 10+ seconds
  • No console errors during connections
  • Clean insertion marker operations
  • No event spam (maintained)

Testing Checklist

Drag Performance

  • Drag block from toolbox → workspace (slow drag, 5+ seconds)
  • Drag block around workspace (slow drag, 10+ seconds)
  • Drag block quickly across workspace
  • Drag multiple blocks in succession

Connection Operations

  • Drag block to connect to another block
  • Check console for errors during connection
  • Verify insertion marker appears/disappears smoothly
  • Verify blocks actually connect properly

Workspace Persistence

  • Add blocks, close tab, reopen → blocks should persist
  • Edit workspace, switch to canvas, back to Logic Builder → no loss
  • Save project, reload → workspace loads correctly

Performance

  • No lag during dragging
  • Console shows reasonable event frequency
  • Project saves at reasonable intervals (not spamming)

Files Modified

  • packages/noodl-editor/src/editor/src/views/BlocklyEditor/BlocklyWorkspace.tsx
    • Replaced blanket debouncing with event filtering
    • Added event type checks for UI-only events
    • Separated UI vs programmatic event handling
    • Increased debounce timeout to 300ms

Rollback Plan

If this fix doesn't work, we can:

  1. Revert to previous debounced approach
  2. Try alternative: disable onChange during gestures using Blockly gesture events
  3. Try alternative: use MutationObserver instead of change events

Learning

Blockly Event System: Blockly fires many event types. Not all need persistence. UI feedback events (drag, select, viewport) should be ignored. Only respond to structural changes (CREATE, DELETE, CHANGE, MOVE completed). The isUiEvent property distinguishes user actions from programmatic changes.

Next Steps

  1. Test the fix - Run through testing checklist above
  2. If successful - Update DRAG-DROP-ISSUE.md with "RESOLVED" status
  3. If unsuccessful - Document what still fails and try alternative approaches
  4. Record in CHANGELOG.md - Document the fix for future reference
  5. Record in LEARNINGS.md - Add to institutional knowledge

Testing Required By: Richard (manual testing in running app) Expected Outcome: Smooth, continuous dragging with no console errors