Skip to content

Group MODE field changes with disconnect events into single undo entry#438

Open
tracygardner wants to merge 1 commit intomainfrom
claude/fix-undo-grouping-PBDof
Open

Group MODE field changes with disconnect events into single undo entry#438
tracygardner wants to merge 1 commit intomainfrom
claude/fix-undo-grouping-PBDof

Conversation

@tracygardner
Copy link
Contributor

@tracygardner tracygardner commented Mar 21, 2026

Summary

This change ensures that when the MODE field is changed on a control block, any block disconnections triggered by validation are grouped together with the field change into a single undo entry, providing a better user experience.

Key Changes

  • Added _modeChangeGroupId property to track event groups opened during MODE field changes
  • Modified the MODE field validator to open a new event group if one isn't already active, allowing subsequent disconnect events to be grouped together
  • Added logic in the onchange handler to close the event group once Blockly fires the corresponding BLOCK_CHANGE event for the MODE field

Implementation Details

The solution works by:

  1. When the MODE field dropdown changes, the validator checks if an event group is already active
  2. If no group exists, it creates one and stores its ID in _modeChangeGroupId
  3. The updateShape_ method then executes, potentially triggering BLOCK_MOVE events from validateAndDisconnectInvalidChain_
  4. Once Blockly fires the BLOCK_CHANGE event for the field change, the onchange handler detects it and closes the event group
  5. This ensures all related events (field change + disconnections) are treated as a single undo operation

This approach respects existing event groups and only creates new ones when necessary, avoiding interference with other event grouping logic.

https://claude.ai/code/session_013VxihhypLXT63EyPWiBk6j

Summary by CodeRabbit

  • Bug Fixes
    • Improved undo/redo handling for the if_clause block MODE field. When users modify the MODE, related block adjustments are now grouped into a single undo action, allowing all connected changes to be reverted together in one step rather than multiple operations.

…orrect undo

When the user changes an if_clause block's MODE dropdown (e.g. IF → ELSE)
while a following block needs to be disconnected, validateAndDisconnectInvalidChain_
was firing BLOCK_MOVE events before Blockly fired the BLOCK_CHANGE for the field.
With no active event group, these landed in separate undo entries, requiring two
Ctrl+Z presses to undo a single user action.

Fix: the dropdown validator now opens an event group when none is already active,
and the onchange handler closes it once the corresponding BLOCK_CHANGE fires. This
ensures the disconnect and the field change are always a single undo entry.

https://claude.ai/code/session_013VxihhypLXT63EyPWiBk6j
@coderabbitai
Copy link

coderabbitai bot commented Mar 21, 2026

📝 Walkthrough

Walkthrough

The if_clause block in blocks/control.js now manages event grouping during MODE field changes. When a MODE dropdown change triggers block disconnections, the code groups the resulting BLOCK_MOVE and BLOCK_CHANGE events into a single undo entry by storing and tracking the event group identifier.

Changes

Cohort / File(s) Summary
Event Grouping for MODE Changes
blocks/control.js
Added _modeChangeGroupId instance property and modified MODE dropdown handler and onchange handler to coordinate event grouping, ensuring block move operations and field changes are combined into a single undo entry.

Sequence Diagram

sequenceDiagram
    participant User
    participant ModeHandler as MODE Handler
    participant EventSys as Blockly Event System
    participant ValidateDisconnect as validateAndDisconnectInvalidChain_
    participant OnChange as onchange Handler

    User->>ModeHandler: Change MODE dropdown
    ModeHandler->>EventSys: Open event group (store _modeChangeGroupId)
    ModeHandler->>ValidateDisconnect: Call validateAndDisconnectInvalidChain_
    ValidateDisconnect->>EventSys: Emit BLOCK_MOVE events
    ModeHandler->>EventSys: Emit BLOCK_CHANGE event for MODE field
    EventSys->>OnChange: Trigger onchange with event details
    OnChange->>EventSys: Detect matching group id
    OnChange->>EventSys: Close event group
    OnChange->>OnChange: Clear _modeChangeGroupId
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A mode change ripples through the blocks,
Events grouped in harmony locks,
One undo now, instead of three,
The if_clause dances gracefully!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: grouping MODE field changes with disconnect events into a single undo entry, which directly matches the primary objective of the pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/fix-undo-grouping-PBDof

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
blocks/control.js (1)

476-483: Guard group creation to real MODE changes only.

At line 480, a group can be opened before confirming this selection will emit a matching BLOCK_CHANGE event. In current Blockly core, if a FieldDropdown validator returns the same value as the current field value, no BLOCK_CHANGE event is emitted. This means cleanup at lines 603-614 may never run, leaving the event group stale.

Suggested hardening
         new Blockly.FieldDropdown(
           [
             ["%{BKY_CONTROLS_IF_MSG_IF}", MODE.IF],
             ["%{BKY_CONTROLS_IF_MSG_ELSEIF}", MODE.ELSEIF],
             ["%{BKY_CONTROLS_IF_MSG_ELSE}", MODE.ELSE],
           ],
           (newValue) => {
+            const currentValue = this.getFieldValue("MODE");
+            const willActuallyChange = newValue !== currentValue;
             // If there's no active event group, open one so that any
             // BLOCK_MOVE events fired by validateAndDisconnectInvalidChain_
             // land in the same undo entry as the BLOCK_CHANGE that Blockly
             // fires for this field after the validator returns.
-            if (!Blockly.Events.getGroup() && !this._modeChangeGroupId) {
+            if (
+              willActuallyChange &&
+              Blockly.Events.isEnabled() &&
+              !Blockly.Events.getGroup() &&
+              !this._modeChangeGroupId
+            ) {
               Blockly.Events.setGroup(true);
               this._modeChangeGroupId = Blockly.Events.getGroup();
             }
             this.updateShape_(newValue);
             return newValue;
           },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@blocks/control.js` around lines 476 - 483, The code currently opens an event
group unconditionally (using Blockly.Events.getGroup / setGroup and
this._modeChangeGroupId) before confirming a real MODE change; if a
FieldDropdown validator returns the same value no BLOCK_CHANGE fires and the
group can be left stale. Modify the logic in the mode-changing flow (where
validateAndDisconnectInvalidChain_ and the dropdown selection are handled) to
only call Blockly.Events.setGroup(...) and assign this._modeChangeGroupId when
the new mode/value is different from the current value (compare against
this.getValue() or the field's current mode) or after the validator confirms a
different value will be applied; keep the existing cleanup code but ensure it
only runs when a group was actually opened (i.e., this._modeChangeGroupId is
set).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@blocks/control.js`:
- Around line 476-483: The code currently opens an event group unconditionally
(using Blockly.Events.getGroup / setGroup and this._modeChangeGroupId) before
confirming a real MODE change; if a FieldDropdown validator returns the same
value no BLOCK_CHANGE fires and the group can be left stale. Modify the logic in
the mode-changing flow (where validateAndDisconnectInvalidChain_ and the
dropdown selection are handled) to only call Blockly.Events.setGroup(...) and
assign this._modeChangeGroupId when the new mode/value is different from the
current value (compare against this.getValue() or the field's current mode) or
after the validator confirms a different value will be applied; keep the
existing cleanup code but ensure it only runs when a group was actually opened
(i.e., this._modeChangeGroupId is set).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 31a502a4-21f3-41bc-b9b2-ef5325a7adb3

📥 Commits

Reviewing files that changed from the base of the PR and between 68d563e and 9d8ba7a.

📒 Files selected for processing (1)
  • blocks/control.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants