Skip to content

feat(HappyHare): replace right-click menu with regular click menu#2437

Merged
meteyou merged 18 commits into
mainsail-crew:developfrom
moggieuk:changetool_menu2
Jun 20, 2026
Merged

feat(HappyHare): replace right-click menu with regular click menu#2437
meteyou merged 18 commits into
mainsail-crew:developfrom
moggieuk:changetool_menu2

Conversation

@moggieuk

@moggieuk moggieuk commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

Description

This PR removes the right-click context menu and replaces it with a regular click. This simplifies operation especially on mobile devices and was advocated by the community. The sensitivity of menu items is set based on current MMU state/capabilities and now necessarily extends to the bypass gate. This PR groups all changes the the context menu including the ability to invoke none gcode command operations like opening the filament editor dialog with the selected gate as default. The cursor type is pointer for gate selection and menu in the main panel

Note: There will be a minor merge conflict with PR #2429 in MmuUnitGate.vue since both modify the same template line. The correct resolution is (note mb-n5, @click handling and cursorType class):

<template>
    <div class="d-flex flex-column align-center" :class="cursorType" @click="handleClickGate" @contextmenu.prevent>
        <div class="d-flex flex-wrap mb-n5 pt-1 position-relative">
            <mmu-unit-gate-spool

Related Tickets & Documents

This PR replaces closed PR #2430

Mobile & Desktop Screenshots/Recordings

Screenshot 2026-02-12 at 3 45 16 AM

[optional] Are there any post-deployment tasks we need to perform?

n/a

@coderabbitai

coderabbitai Bot commented Feb 11, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Refactored MMU gate interaction from long-press/contextmenu handlers to unified click-based flow. Gate components expose handleClickGate and emit edit-filament events that propagate through MmuPanel, which sets initialEditGate prop on the edit dialog for preselection. Context menu actions route through item-based model with per-item loading and disabled states. Spool selection APIs simplified and hover styling updated. Data contracts extended with crossload capability flag.

Changes

MMU Gate Edit Flow

Layer / File(s) Summary
MMU type contract extension
src/components/mixins/mmu.ts
MmuMachineUnit interface extended with can_crossload: boolean flag to track crossload capability.
Gate interaction refactoring
src/components/panels/Mmu/MmuUnitGate.vue
Replaces long-press/contextmenu handlers with handleClickGate() entry point. Implements item-based context menu system with ContextMenuItem types, contextMenuItems, contextMenuHeader, runMenuItem, and isItemDisabled. Adds computed helpers (cursorType, canCrossload, isLoaded, isSelectedGate), makes selectGate() private, removes gateCommand() dispatcher, emits edit-filament events. Updates CSS with cursor classes (.gate-selection, .gate-menu).
Spool component updates
src/components/panels/Mmu/MmuUnitGateSpool.vue
Removes selectGate() method and direct click binding from SVG. Updates svgClasses to remove listener-derived hasSelectGate class logic. Removes hasSelectGateListener getter. Updates hover CSS from svg.hasSelectGate:hover to svg:hover.
Edit-filament event flow
src/components/panels/MmuPanel.vue
Adds initialEditGate state and editFilament(gateIndex) handler to set state and open dialog. Binds :initial-gate="initialEditGate" to dialog and resets state on dialog close. Wires mmu-unit edit-filament event to handler.
Localization
src/locales/en.json
Added MmuPanel.Active.ButtonChangeTool translation key with value "Change Tool".

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A click upon a gate so small,
Menus bloom and heed the call!
Filament whispers: "edit me,"
Dialog opens: preselected, free! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: replacing right-click context menus with regular click menus and enabling additional non-gcode operations through the menu system.
Description check ✅ Passed The description is directly related to the changeset, explaining the motivation (mobile-friendly simplification), implementation details (dynamic menu sensitivity based on state), and the specific changes made to the context menu interaction model.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/components/panels/Mmu/MmuUnitGate.vue`:
- Around line 212-215: The handleClickGate method calls
selectGate(this.gateIndex) but selectGate is defined to take no arguments (it
reads this.gateIndex itself); remove the extraneous parameter so call
selectGate() instead (or alternatively update selectGate to accept an index and
use it everywhere, but prefer the simpler fix of changing handleClickGate to
call selectGate() and ensure no other callers rely on the old signature).
🧹 Nitpick comments (2)
src/components/dialogs/MmuEditGateMapDialog.vue (1)

83-92: Minor: redundant undefined check.

The initialGate prop is typed as number | null with a default of null, so it can never be undefined. The !== undefined guard on line 87 is unnecessary.

Suggested simplification
     `@Watch`('showDialog')
     onShowDialogChanged(val: boolean) {
         if (!val) return

-        if (this.initialGate !== null && this.initialGate !== undefined) {
+        if (this.initialGate !== null) {
             this.selectedGate = this.initialGate
         } else {
             this.selectedGate = TOOL_GATE_UNKNOWN
         }
     }
src/components/panels/Mmu/MmuUnitGate.vue (1)

8-11: No-op @select-spool handler used solely for CSS hover class.

The () => {} handler exists only to make hasSelectSpoolListener return true in MmuUnitGateSpool, which drives the .hasSelectSpool hover CSS class. Since the actual click is handled by the parent div's @click="handleClickGate" via DOM bubbling, this works but is indirect. A dedicated boolean prop (e.g., :show-hover="true") would be more explicit.

That said, if this pattern was already used before the rename, keeping it consistent is reasonable.

Comment thread src/components/panels/Mmu/MmuUnitGate.vue

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/panels/Mmu/MmuUnitGateSpool.vue`:
- Around line 216-227: The current checks in the isEspoolerRewind and
isEspoolerAssist getters only verify that the mmuEspoolers array exists, but an
empty array is truthy and accessing a non-existent index returns undefined,
causing the condition to fail and incorrectly skip to the legacy branch. Replace
the simple existence checks with optional chaining that verifies the actual
indexed value exists: change `if (this.mmuEspoolers)` to `if
(this.mmuEspoolers?.[this.gateIndex])` in both the isEspoolerRewind getter and
the isEspoolerAssist getter, so the condition only returns true when an actual
value is present at the gateIndex position.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6db587b5-784a-4cbf-a999-bb695022b344

📥 Commits

Reviewing files that changed from the base of the PR and between c15a85c and f994b8c.

📒 Files selected for processing (4)
  • src/components/panels/Mmu/MmuUnitGate.vue
  • src/components/panels/Mmu/MmuUnitGateSpool.vue
  • src/components/panels/MmuPanel.vue
  • src/locales/en.json
✅ Files skipped from review due to trivial changes (1)
  • src/locales/en.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/panels/MmuPanel.vue
  • src/components/panels/Mmu/MmuUnitGate.vue

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/panels/Mmu/MmuUnitGateSpool.vue`:
- Around line 216-227: The current checks in the isEspoolerRewind and
isEspoolerAssist getters only verify that the mmuEspoolers array exists, but an
empty array is truthy and accessing a non-existent index returns undefined,
causing the condition to fail and incorrectly skip to the legacy branch. Replace
the simple existence checks with optional chaining that verifies the actual
indexed value exists: change `if (this.mmuEspoolers)` to `if
(this.mmuEspoolers?.[this.gateIndex])` in both the isEspoolerRewind getter and
the isEspoolerAssist getter, so the condition only returns true when an actual
value is present at the gateIndex position.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6db587b5-784a-4cbf-a999-bb695022b344

📥 Commits

Reviewing files that changed from the base of the PR and between c15a85c and f994b8c.

📒 Files selected for processing (4)
  • src/components/panels/Mmu/MmuUnitGate.vue
  • src/components/panels/Mmu/MmuUnitGateSpool.vue
  • src/components/panels/MmuPanel.vue
  • src/locales/en.json
✅ Files skipped from review due to trivial changes (1)
  • src/locales/en.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/panels/MmuPanel.vue
  • src/components/panels/Mmu/MmuUnitGate.vue
🛑 Comments failed to post (1)
src/components/panels/Mmu/MmuUnitGateSpool.vue (1)

216-227: ⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect the mmuEspoolers contract/defaults and related legacy espooler state.
# Expectation: If mmuEspoolers can be [] or missing a gate entry, MmuUnitGateSpool should fall back per gate instead of returning immediately.

rg -n -C4 '\b(mmuEspoolers|espooler_active)\b' --glob '*.ts' --glob '*.vue'

Repository: mainsail-crew/mainsail

Length of output: 2754


🏁 Script executed:

# Find the type definition of espooler in the mmu state/mixin
rg -n 'espooler' src/components/mixins/mmu.ts -A2 -B2

Repository: mainsail-crew/mainsail

Length of output: 437


🏁 Script executed:

# Search for espooler type definitions and interfaces
rg -n 'type.*[Ee]spoiler|interface.*[Ee]spoiler' --glob '*.ts'

Repository: mainsail-crew/mainsail

Length of output: 48


🏁 Script executed:

# Find MmuEspoolerState definition
rg -n 'MmuEspoolerState' --glob '*.ts' -B2 -A2

Repository: mainsail-crew/mainsail

Length of output: 928


Check mmuEspoolers for both existence and value before accessing indexed properties.

The property espooler is typed as MmuEspoolerState[] (optional array). Lines 217 and 224 use if (this.mmuEspoolers) before indexing, but an empty array is truthy; accessing [this.gateIndex] on an empty or incomplete array returns undefined, causing the condition to return false instead of falling back to the legacy espooler_active branch.

Fix by checking for a defined value at the index:

if (this.mmuEspoolers?.[this.gateIndex]) return this.mmuEspoolers[this.gateIndex] === 'rewind'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/panels/Mmu/MmuUnitGateSpool.vue` around lines 216 - 227, The
current checks in the isEspoolerRewind and isEspoolerAssist getters only verify
that the mmuEspoolers array exists, but an empty array is truthy and accessing a
non-existent index returns undefined, causing the condition to fail and
incorrectly skip to the legacy branch. Replace the simple existence checks with
optional chaining that verifies the actual indexed value exists: change `if
(this.mmuEspoolers)` to `if (this.mmuEspoolers?.[this.gateIndex])` in both the
isEspoolerRewind getter and the isEspoolerAssist getter, so the condition only
returns true when an actual value is present at the gateIndex position.

@meteyou meteyou changed the title feat(HappyHare): Replace right-click context menu with regular click menus and completes operations feat(HappyHare): replace right-click menu with regular click menu Jun 20, 2026
@meteyou meteyou merged commit 9e0d31b into mainsail-crew:develop Jun 20, 2026
1 check passed
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