Skip to content

Conversation

@protocol113
Copy link

@protocol113 protocol113 commented Sep 30, 2025

  • Progressive spacebar workflow: Check checkbox → Start wizard → Spin motors
  • Hold spacebar to spin motors, release to stop
  • Number keys 1-8 toggle individual motor directions
  • Added visual tooltip showing keyboard shortcuts
  • Properly scoped keyboard handlers to dialog visibility
  • Fixed settings persistence when closing dialog via any method

Improves user workflow by reducing mouse interaction and enabling faster motor direction configuration.

Summary by CodeRabbit

  • New Features
    • Keyboard shortcuts for ESC D-shot direction control: Space to spin/stop motors and keys 1–8 to toggle individual motor directions.
    • Keyboard shortcuts are enabled when the ESC direction dialog opens to ensure immediate responsiveness.
    • Visible keyboard-shortcuts tooltip added to the wizard for user guidance.
    • Safety: motors automatically stop if the window loses focus while Space is held.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 30, 2025

Walkthrough

Adds keyboard-driven controls and UI hints to the ESC DShot direction wizard: Space starts/stops spinning, keys 1–8 toggle motor directions; introduces keyboard lifecycle and safety (window blur); adds a keyboard shortcuts tooltip; exposes open() to enable global keyboard handling when the dialog opens.

Changes

Cohort / File(s) Summary
UI Template
src/components/EscDshotDirection/Body.html
Added a static .keyboard-shortcuts-tooltip block inside the EscDshotDirection wizard dialog to display shortcuts.
Component Logic
src/components/EscDshotDirection/EscDshotDirectionComponent.js
Added comprehensive keyboard handling: global and wizard keyboard lifecycles (_enableGlobalKeyboard/_disableGlobalKeyboard), capture-phase wizard key handlers (_handleWizardKeyDown/_handleWizardKeyUp), spacebar press/release flows, window blur safety, motor direction toggling, wiring to wizard start/stop, and a new public open(); enhanced close() to disable keyboard handlers and stop/deactivate motors.
Styling
src/css/tabs/motors.less
Added .keyboard-shortcuts-tooltip styles (kbd, .shortcut-item, .shortcut-separator, strong).
Dialog Init
src/js/tabs/motors.js
Calls escDshotDirectionComponent.open() before showing the ESC DShot direction modal to activate global keyboard handling.

Sequence Diagram

sequenceDiagram
    actor User
    participant Dialog as EscDshotDirection Dialog
    participant Component as EscDshotDirection Component
    participant Motors as Motor Driver

    User->>Dialog: Open modal
    Dialog->>Component: open()
    Component->>Component: _enableGlobalKeyboard()

    Note right of Component: Wizard active -> capture-phase handlers enabled

    User->>Component: Press Space
    Component->>Component: _handleWizardKeyDown (Space)
    Component->>Motors: Spin all motors

    User->>Component: Press key 1-8
    Component->>Component: _toggleMotorDirection()
    Component->>Motors: Toggle selected motor direction

    User->>Component: Release Space
    Component->>Component: _handleWizardKeyUp (Space)
    Component->>Motors: Stop all motors

    User->>Dialog: Close modal
    Dialog->>Component: close()
    Component->>Component: _disableKeyboardControl()
    Component->>Component: _disableGlobalKeyboard()
    Component->>Motors: Stop motors and deactivate
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay special attention to src/components/EscDshotDirection/EscDshotDirectionComponent.js (multiple state flags, capture-phase listeners, blur handling).
  • Verify interactions between global vs. wizard-specific keyboard handlers, and motor stop logic across spacebar release, blur, and close().

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive The description covers key features (spacebar workflow, number keys, visual tooltip, keyboard scoping, settings persistence) but is missing required template sections and does not follow the repository's PR description template format. Follow the repository's PR description template by removing boilerplate text, adding issue references (Fixes #...), and ensuring the description adheres to the guidelines.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly summarizes the main change: adding keyboard shortcuts to the Motor Direction wizard, which matches the PR's primary objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58e983f and 7f9b20f.

📒 Files selected for processing (2)
  • src/components/EscDshotDirection/Body.html (1 hunks)
  • src/components/EscDshotDirection/EscDshotDirectionComponent.js (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-19T20:42:20.332Z
Learnt from: haslinghuis
Repo: betaflight/betaflight-configurator PR: 4510
File: src/js/msp.js:384-391
Timestamp: 2025-09-19T20:42:20.332Z
Learning: Complex MSP duplicate handling fixes in Betaflight Configurator can cause infinite loading messages when changing tabs due to disruption of the callback resolution mechanism. Simple code-only duplicate detection (using this.callbacks.some((instance) => instance.code === code)) is the safer approach that preserves tab switching functionality.

Applied to files:

  • src/components/EscDshotDirection/EscDshotDirectionComponent.js
📚 Learning: 2025-09-19T20:41:44.286Z
Learnt from: haslinghuis
Repo: betaflight/betaflight-configurator PR: 4510
File: src/js/msp.js:384-391
Timestamp: 2025-09-19T20:41:44.286Z
Learning: When fixing MSP duplicate handling in Betaflight Configurator, avoid complex changes to callback resolution mechanisms as they can break tab switching functionality. Simple duplicate detection based on code and payload size is safer than complex requestKey-based approaches.

Applied to files:

  • src/components/EscDshotDirection/EscDshotDirectionComponent.js
🧬 Code graph analysis (1)
src/components/EscDshotDirection/EscDshotDirectionComponent.js (1)
test/tabs/cli.test.js (2)
  • event (30-30)
  • event (37-37)
🔇 Additional comments (10)
src/components/EscDshotDirection/Body.html (1)

28-30: LGTM! Keyboard shortcuts tooltip properly placed.

The tooltip is correctly positioned within the wizard dialog section, making it visible throughout the wizard workflow (from wizard start through motor spinning). The i18n key ensures proper internationalization.

src/components/EscDshotDirection/EscDshotDirectionComponent.js (9)

29-39: LGTM! Keyboard state and handler bindings properly initialized.

The keyboard control state flags are correctly initialized, and all event handlers are properly bound in the constructor to preserve the this context. This is critical for event handlers to access instance properties and methods correctly.


311-360: LGTM! Flow-through logic correctly implements progressive spacebar workflow.

The spacebar progression through safety checkbox → start wizard → spin motors is correctly implemented:

  1. Step 1 (lines 328-334): Checks the safety checkbox if unchecked, then continues (no return)
  2. Step 2 (lines 337-342): Starts the wizard (hiding warning, showing main content), then continues (no return)
  3. Step 3 (lines 345-356): Spins motors and sets _spacebarPressed = true to track the held keydown

The flow-through design allows a single spacebar keydown to progress through all stages, then the keyup event stops the motors—implementing the "hold spacebar to spin, release to stop" behavior described in the PR objectives. The visibility checks are correct: Steps 1-2 check _domWarningContentBlock, while Step 3 checks _domMainContentBlock (which becomes visible after Step 2's DOM changes via _toggleMainContent).


362-384: LGTM! Keyboard control lifecycle properly manages event listeners.

The enable/disable methods correctly:

  • Use capture phase (true as third parameter) to ensure reliable event handling before propagation
  • Include a guard against duplicate binding (line 363)
  • Add window blur listener for safety (properly removing it in disable)
  • Clean up all event listeners and reset state when disabled

The capture phase is critical here to prevent other UI elements from stopping propagation before motor control handlers execute.


386-432: LGTM! Wizard keyboard handlers correctly implement motor control.

The wizard keyboard logic properly handles:

  • Spacebar: Hold to spin, release to stop
    • Line 397: !event.repeat prevents multiple spin commands while key is held
    • _spacebarPressed flag synchronizes keydown/keyup events
  • Number keys 1–8: Toggle individual motor directions (lines 405-413)
    • Bounds check ensures motorIndex < _numberOfMotors
    • Repeat prevention avoids unintended toggles
  • Keyup handling: Only processes spacebar release when a matching keydown occurred (line 427 checks _spacebarPressed)

Guard checks at the start of each handler (lines 388-390, 418-420) ensure events are only processed when keyboard control is active and the wizard is running.


443-449: Excellent safety feature: window blur handler prevents runaway motors.

The _handleWindowBlur method is a critical safety mechanism that stops motors if the user switches windows while holding the spacebar. This prevents motors from continuing to spin if focus is lost (e.g., Alt-Tab, clicking another window), which could be dangerous during testing.

The implementation correctly:

  • Checks if spacebar is currently pressed (line 445)
  • Resets the state flag (line 446)
  • Calls the full stop/cleanup sequence via _handleSpacebarRelease (line 447)

451-462: LGTM! Motor direction toggle correctly mirrors button click behavior.

The _toggleMotorDirection method correctly:

  • Reads the current direction from the button's CSS class state (line 453)
  • Toggles between normal and reverse directions (lines 455-461)
  • Updates both the visual state (button class) and the motor command

This logic mirrors _wizardMotorButtonClick (lines 272-285), maintaining consistency between keyboard and mouse interactions.


464-467: LGTM! Public open() method correctly exposes dialog lifecycle.

The new public API method properly activates global keyboard handling when the dialog is displayed. This design:

  • Separates dialog initialization (constructor) from activation (open)
  • Enables spacebar progression from the moment the dialog appears
  • Aligns with the calling pattern in motors.js (per AI summary)

469-492: LGTM! Close sequence correctly preserves pending motor commands.

The cleanup sequence is carefully ordered to ensure pending direction-change commands complete:

  1. Lines 471-472: Disable keyboard handlers first to prevent new input
  2. Line 477: Deactivate wizard buttons (if active) without clearing the wizard flag yet
  3. Line 481: Stop motors using stopAllMotors() (adds stop to queue, preserves pending commands)
  4. Line 485: Deactivate motor driver (drains queue naturally, completing direction + save commands)
  5. Line 488: Clear wizard flag after motor driver deactivation
  6. Line 491: Reset GUI state

Based on past review comments, the critical issue of using stopAllMotorsNow() (which cleared the queue) was fixed by switching to stopAllMotors(), ensuring direction changes aren't lost during shutdown.


551-570: LGTM! Keyboard control properly integrated into wizard lifecycle.

The wizard start/stop methods now correctly enable/disable keyboard control:

  • Lines 568-569: _onSpinWizardButtonClicked sets _isWizardActive = true and enables keyboard control when the wizard begins spinning
  • Lines 579-580: _onStopWizardButtonClicked disables keyboard control and clears _isWizardActive when the wizard stops

This creates clean symmetry: keyboard shortcuts are only active during the wizard's spinning phase, and the _isWizardActive flag gates keyboard event processing in the handlers.

Also applies to: 572-581


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Contributor

@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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/components/EscDshotDirection/EscDshotDirectionComponent.js (1)

463-486: Carefully ordered cleanup sequence with good explanatory comments.

The close() method correctly sequences cleanup operations to ensure pending motor direction commands complete before the component fully resets. The comments clearly explain the reasoning behind each step.

Consider adding an idempotency guard at the start of the method to prevent issues if close() is called multiple times rapidly (e.g., by both ESC key handler and close button).

Apply this diff to add an idempotency guard:

 close() {
+    // Guard against multiple close calls
+    if (!this._globalKeyboardActive && !this._keyboardEventHandlerBound) {
+        return;
+    }
+
     // Disable keyboard handlers first to prevent any new input
     this._disableKeyboardControl();
     this._disableGlobalKeyboard();
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 105aa26 and e842a51.

📒 Files selected for processing (4)
  • src/components/EscDshotDirection/Body.html (1 hunks)
  • src/components/EscDshotDirection/EscDshotDirectionComponent.js (3 hunks)
  • src/css/tabs/motors.less (1 hunks)
  • src/js/tabs/motors.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/EscDshotDirection/EscDshotDirectionComponent.js (1)
test/tabs/cli.test.js (2)
  • event (30-30)
  • event (37-37)
🔇 Additional comments (8)
src/css/tabs/motors.less (1)

195-237: LGTM! Clean CSS implementation for the keyboard shortcuts tooltip.

The styling is well-structured with consistent use of CSS variables for theming, proper flexbox layout, and appropriate visual hierarchy. The kbd element styling clearly distinguishes keyboard keys from regular text.

src/js/tabs/motors.js (1)

1356-1356: Good addition! Ensures proper initialization order.

Calling escDshotDirectionComponent.open() before showModal() ensures the global keyboard handler is enabled when the dialog becomes visible, which is necessary for the spacebar workflow to function correctly.

src/components/EscDshotDirection/Body.html (1)

30-36: LGTM! Clear and well-structured keyboard shortcuts tooltip.

The HTML structure is semantic and the placement within the wizard spinning section is appropriate. The tooltip provides clear, concise instructions for keyboard shortcuts with proper use of <kbd> elements.

src/components/EscDshotDirection/EscDshotDirectionComponent.js (5)

29-38: LGTM! Proper event handler binding and state initialization.

The constructor correctly initializes keyboard state flags and binds event handlers to preserve the correct this context. The comment on line 35 correctly emphasizes the critical nature of binding for event handlers.


298-359: Solid implementation of the progressive spacebar workflow.

The global keyboard handler correctly implements the four-step progression: checkbox → start wizard → spin motors → wizard control. The visibility check (lines 318-324) ensures keyboard input is only processed when the dialog is actually visible, preventing unexpected behavior.

The special handling on line 352 (this._spacebarPressed = true) correctly marks the spacebar as pressed during the transition to wizard control, ensuring consistent state when the wizard handler takes over.


390-456: Well-implemented wizard keyboard controls with proper safeguards.

The keyboard handlers correctly implement spacebar hold-to-spin and number key direction toggling. Good use of:

  • State flags to gate behavior (_isKeyboardControlEnabled && _isWizardActive)
  • !event.repeat to prevent repeated triggers when holding keys
  • Bounds checking for motor indices (line 414)

The _toggleMotorDirection method correctly synchronizes both UI state (button class) and motor driver state.


458-461: LGTM! Clean initialization hook.

The open() method provides a clean initialization point for enabling global keyboard handling when the dialog is opened. This is correctly called from motors.js before showModal().


545-574: Proper keyboard lifecycle integration with wizard flow.

The keyboard control is correctly enabled when the wizard starts spinning (lines 562-563) and disabled when it stops (lines 573-574). The _isWizardActive flag properly gates wizard keyboard functionality and ensures the handlers only process input during the appropriate phase.

- Progressive spacebar workflow: Check checkbox → Start wizard → Spin motors
- Hold spacebar to spin motors, release to stop
- Number keys 1-8 toggle individual motor directions
- Added visual tooltip showing keyboard shortcuts
- Properly scoped keyboard handlers to dialog visibility
- Fixed settings persistence when closing dialog via any method

Improves user workflow by reducing mouse interaction and enabling
faster motor direction configuration.
@protocol113 protocol113 force-pushed the feature/keyboard-shortcuts-motor-wizard branch from e842a51 to 494c252 Compare September 30, 2025 02:49
@sonarqubecloud
Copy link

Copy link
Contributor

@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.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e842a51 and 494c252.

📒 Files selected for processing (4)
  • src/components/EscDshotDirection/Body.html (1 hunks)
  • src/components/EscDshotDirection/EscDshotDirectionComponent.js (3 hunks)
  • src/css/tabs/motors.less (1 hunks)
  • src/js/tabs/motors.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/js/tabs/motors.js
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/EscDshotDirection/EscDshotDirectionComponent.js (1)
test/tabs/cli.test.js (2)
  • event (30-30)
  • event (37-37)
🔇 Additional comments (11)
src/components/EscDshotDirection/Body.html (1)

30-36: LGTM! Clean UI addition for keyboard shortcuts.

The tooltip is well-structured and appropriately placed within the spinning wizard section. The use of <kbd> tags for keyboard keys follows HTML best practices.

src/components/EscDshotDirection/EscDshotDirectionComponent.js (9)

29-39: LGTM! Proper method binding for event handlers.

All keyboard event handlers are correctly bound in the constructor, including the _handleWindowBlur fix from the previous review. This ensures proper this context preservation.


299-360: LGTM! Well-structured progressive workflow.

The global keyboard handler implements a clean state machine:

  1. Check safety checkbox if unchecked
  2. Start wizard if checked
  3. Spin motors if wizard ready
  4. Hand off to wizard keyboard handler when active

The dialog visibility checks prevent input when dialog is hidden, and the spacebar state management at line 353 correctly handles the transition case.


362-384: LGTM! Proper event listener lifecycle management.

The keyboard control enable/disable methods correctly:

  • Use capture phase (true) for reliable event handling
  • Clean up all listeners including the blur handler (fix from previous review)
  • Reset spacebar state on disable

386-431: LGTM! Robust wizard keyboard handling.

The wizard keyboard handlers properly:

  • Guard with state checks (_isKeyboardControlEnabled, _isWizardActive)
  • Prevent default and stop propagation
  • Use event.repeat checks to prevent key-hold spamming
  • Handle spacebar press/release cycle correctly
  • Support number keys 1-8 for motor direction toggles with bounds checking

433-447: LGTM! Clean separation of concerns.

The helper methods provide clear, single-responsibility functions for spacebar actions and window blur safety. The blur handler correctly stops motors if spacebar is held when the window loses focus.


449-460: LGTM! Motor direction toggle implementation.

The _toggleMotorDirection method correctly mirrors the button click behavior from _wizardMotorButtonClick (lines 272-285), maintaining consistency between mouse and keyboard interactions.


462-465: LGTM! Dialog initialization hook.

The open() method provides a clean entry point for enabling global keyboard handling when the dialog opens.


549-568: LGTM! Wizard start enables keyboard control.

The _onSpinWizardButtonClicked method correctly:

  • Resets motor button states
  • Sets all motors to normal direction
  • Shows spinning wizard UI
  • Spins all motors
  • Activates wizard motor buttons
  • Enables keyboard control and sets wizard active flag (lines 566-567)

570-579: LGTM! Wizard stop cleanly disables keyboard control.

The _onStopWizardButtonClicked method correctly disables keyboard control and clears the wizard active flag, ensuring keyboard handlers stop processing input.

src/css/tabs/motors.less (1)

196-237: LGTM! Well-structured CSS for keyboard shortcuts tooltip.

The styling:

  • Uses CSS variables consistently with the codebase theme system
  • Implements proper visual hierarchy (border accent, contrasting background)
  • Includes responsive flex wrapping
  • Provides appropriate spacing and visual polish (box-shadow on kbd elements)
  • Follows LESS nesting conventions used throughout the file

Copy link
Contributor

@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.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/components/EscDshotDirection/EscDshotDirectionComponent.js (2)

377-384: Consider consistent cleanup in _disableKeyboardControl().

Line 383 sets _spacebarPressed = false during cleanup, but unlike _handleWindowBlur() (lines 442-448), it does not call _handleSpacebarRelease() to stop the motors. While this is safe because close() explicitly calls stopAllMotorsNow() at line 480, the inconsistency makes the cleanup path less obvious.

For clarity and maintainability, consider adding the same safety pattern from _handleWindowBlur:

 _disableKeyboardControl() {
     document.removeEventListener("keydown", this._handleWizardKeyDown, true);
     document.removeEventListener("keyup", this._handleWizardKeyUp, true);
     window.removeEventListener("blur", this._handleWindowBlur);
+
+    // Stop motors if spacebar was held during cleanup
+    if (this._spacebarPressed) {
+        this._spacebarPressed = false;
+        this._handleSpacebarRelease();
+    }
+
     this._keyboardEventHandlerBound = false;
     this._isKeyboardControlEnabled = false;
-    this._spacebarPressed = false;
 }

This ensures motors are stopped via a consistent code path regardless of how keyboard control is disabled.

Note: This suggestion was previously discussed in past review comments and remains relevant for code clarity.


450-461: Consider extracting shared direction-toggle logic.

The motor direction toggle logic in _toggleMotorDirection (lines 450-461) duplicates logic from _wizardMotorButtonClick (lines 272-285). Both methods:

  1. Check if the button has the PUSHED_BUTTON_CLASS
  2. Toggle the class
  3. Call setEscSpinDirection with the appropriate direction

Extract the shared logic to reduce duplication:

_toggleMotorDirection(motorIndex) {
    const button = this._wizardMotorButtons[motorIndex];
    this._setMotorDirectionFromButton(button, motorIndex);
}

_wizardMotorButtonClick(button, motorIndex) {
    this._deactivateWizardMotorButtons();
    this._setMotorDirectionFromButton(button, motorIndex);
    this._activateWizardMotorButtons(this._buttonTimeoutMs);
}

_setMotorDirectionFromButton(button, motorIndex) {
    const currentlyDown = button.hasClass(EscDshotDirectionComponent.PUSHED_BUTTON_CLASS);
    
    if (currentlyDown) {
        button.removeClass(EscDshotDirectionComponent.PUSHED_BUTTON_CLASS);
        this._motorDriver.setEscSpinDirection(motorIndex, DshotCommand.dshotCommands_e.DSHOT_CMD_SPIN_DIRECTION_1);
    } else {
        button.addClass(EscDshotDirectionComponent.PUSHED_BUTTON_CLASS);
        this._motorDriver.setEscSpinDirection(motorIndex, DshotCommand.dshotCommands_e.DSHOT_CMD_SPIN_DIRECTION_2);
    }
}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 494c252 and 50d56ea.

📒 Files selected for processing (2)
  • src/components/EscDshotDirection/EscDshotDirectionComponent.js (3 hunks)
  • src/components/EscDshotDirection/EscDshotDirectionMotorDriver.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/EscDshotDirection/EscDshotDirectionComponent.js (1)
test/tabs/cli.test.js (2)
  • event (30-30)
  • event (37-37)
🔇 Additional comments (6)
src/components/EscDshotDirection/EscDshotDirectionComponent.js (5)

29-39: LGTM: Constructor keyboard state initialization.

The keyboard control state flags and handler bindings are properly initialized. Binding methods in the constructor ensures correct this context in event handlers, which is critical for the keyboard control lifecycle.


311-360: Review spacebar state transition at line 353.

At line 353, _spacebarPressed is set to true when transitioning to wizard control while the spacebar key is physically down. This creates a potential edge case:

Scenario: User presses spacebar on Step 3 (line 354 calls _onSpinWizardButtonClicked()) → wizard activates and enables keyboard control (line 568) → but the user is still holding spacebar → when they release it, _handleWizardKeyUp (line 424) will process the release.

Issue: The global handler sets _spacebarPressed = true, but _handleSpacebarPress() (line 435) isn't called, so spinAllMotors() is only called once on line 562 via _onSpinWizardButtonClicked(). This appears correct for the intended flow, but the state management is subtle.

Verify this sequence works as expected:

  1. User presses Space on "Spin Wizard" button visibility
  2. _handleGlobalKeyDown sets _spacebarPressed = true and calls _onSpinWizardButtonClicked()
  3. _onSpinWizardButtonClicked() enables keyboard control and calls spinAllMotors()
  4. User releases Space → _handleWizardKeyUp detects _spacebarPressed = true and calls _handleSpacebarRelease() → motors stop

Test this flow manually to ensure the state transition is clean and motors behave as expected (spin when pressed, stop when released).


386-432: LGTM: Wizard keyboard event handlers.

The keyboard event handlers are well-implemented with appropriate safeguards:

  • Proper guard conditions checking both _isKeyboardControlEnabled and _isWizardActive
  • event.repeat checks prevent multiple triggers when holding keys (lines 397, 405)
  • Bounds checking for motor indices (line 410)
  • Proper use of preventDefault() and stopPropagation() to prevent conflicts
  • Clear inline documentation of the stopAllMotorsNow() behavior (lines 422-423)

463-491: LGTM: Dialog lifecycle methods with proper cleanup ordering.

The open() and close() methods implement a careful cleanup sequence:

  1. Line 469-471: Disables keyboard handlers first to prevent new input
  2. Line 475-477: Deactivates wizard buttons (if active)
  3. Line 480: Stops motors (now preserves pending direction changes)
  4. Line 484: Deactivates motor driver (drains queue when empty)
  5. Line 487: Clears wizard flag
  6. Line 490: Resets GUI

This ordering ensures pending direction-change commands complete before shutdown, addressing the critical issue raised in past reviews. The inline comments (lines 473-474, 479, 482-483, 486) clearly document the rationale.

Note: This implementation relies on the modified behavior of stopAllMotorsNow() in EscDshotDirectionMotorDriver.js to preserve queued commands.


550-580: LGTM: Wizard button handlers with keyboard lifecycle integration.

The wizard start and stop handlers correctly integrate the keyboard control lifecycle:

  • Lines 566-568: When the wizard starts spinning, _isWizardActive is set and keyboard control is enabled, allowing spacebar and number key shortcuts.
  • Lines 577-579: When the wizard stops, keyboard control is disabled and _isWizardActive is cleared.

This ties the keyboard shortcut functionality directly to the wizard's active state, ensuring shortcuts are only available when appropriate.

src/components/EscDshotDirection/EscDshotDirectionMotorDriver.js (1)

48-61: Both stopAllMotors() and stopAllMotorsNow() are now identical—verify this is intentional.

The methods on lines 44-45 and 48-61 both call _spinAllMotors(this._motorStopValue) with no distinguishing logic. The comment on stopAllMotorsNow() states the method preserves the queue "without clearing pending commands," which means clear() was removed. However, if both methods now preserve the queue identically, then:

  1. stopAllMotors() (lines 176, 223 in normal button handlers) will now also delay up to ~100ms during direction changes, not just spacebar release.
  2. No emergency-stop mechanism exists. All stop paths queue-preserve; there's no way to force immediate motor halt if needed.

The ~100ms spin trade-off for direction-change preservation is documented and reasonable, but the identical implementation suggests either:

  • stopAllMotors() should call clear() then _spinAllMotors() for immediate stops in normal workflows, OR
  • One method is redundant and should be removed

Confirm the intended behavior difference between these two methods.

Use stopAllMotors() instead of stopAllMotorsNow() to avoid clearing the
command queue. This ensures direction-change and SAVE_SETTINGS commands
complete even when spacebar is released or dialog is closed.

stopAllMotorsNow() retains its original behavior (clears queue) for
emergency stop scenarios where immediate halt is required.
@protocol113 protocol113 force-pushed the feature/keyboard-shortcuts-motor-wizard branch from 50d56ea to 58e983f Compare November 10, 2025 02:24
Copy link
Contributor

@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.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50d56ea and 58e983f.

📒 Files selected for processing (1)
  • src/components/EscDshotDirection/EscDshotDirectionComponent.js (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/EscDshotDirection/EscDshotDirectionComponent.js (1)
test/tabs/cli.test.js (2)
  • event (30-30)
  • event (37-37)

- Allow single spacebar press to cascade through safety check -> start wizard -> spin motors
- Make spacebar release fully stop wizard (same as Stop Motors button)
- Move keyboard shortcuts tooltip outside SpinningWizard div so it's visible before motors start
- Refactor tooltip to use i18n instead of hardcoded text

This improves discoverability and provides a more intuitive toggle behavior for the spacebar shortcut.
@sonarqubecloud
Copy link

@github-actions
Copy link
Contributor

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

Projects

Status: App

Development

Successfully merging this pull request may close these issues.

4 participants