Skip to content

Conversation

@clounie
Copy link
Contributor

@clounie clounie commented Dec 28, 2025

Resolves #2719

  • Ran npm run format
  • Ran npm run lint:fix
  • Ran npm test
  • Manually tested fixes

Description:

What problem(s) was I solving?

On Mac, Ctrl+Click is commonly used as a substitute for right-click to open context menus. However, in OpenFront, Ctrl+Click was triggering both the context menu, AND causing an attack.

This effectively prevented Mac users from being able to ally with bots/nations, as trying to ally would cause you to attack them.

What changes did I make?

  • Ctrl+Click on Mac no longer triggers an attack, allowing players to properly interact with the alliance system

How I implemented it

  1. Added an isMac() method to InputHandler which encapsulates the "is mac" logic, and lets it be mocked during tests
  2. In the onPointerUp handler, added a check: if on Mac and ControlLeft is held, emit a ContextMenuEvent, and then return (instead of continuing to also create a MouseUpEvent)
  3. Extracted magic numbers for mouse button checks into descriptive helper methods (isMiddleMouseButton, isNonLeftMouseButton) for improved code clarity
  4. Added clarifying comments throughout the pointer event handlers

Last, alphabetized the .gitignore file and organized it into "Folders" and "Files" sections to make it easier to read.

How to verify it

Manual Testing

  • On a Mac, hold Ctrl and left-click on another nation - verify the context menu opens (not an attack)
  • On a Mac, right-click should still open the context menu as expected
  • On Windows/Linux, Ctrl+Click continue to work as before (modifier key for build menu if configured)
  • Regular left-click still triggers attacks/interactions as expected

Automated Testing

  • New unit test added: Mac Ctrl+Click Context Menu - verifies that Ctrl+Click on Mac emits ContextMenuEvent instead of MouseUpEvent

Description for the changelog

Fixed Ctrl+Click on Mac to properly open the context menu instead of triggering an attack, restoring the ability for Mac users to form alliances with other nations.

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced

Please put your Discord username so you can be contacted if a bug or regression is found:

Terekhov

@clounie clounie requested a review from a team as a code owner December 28, 2025 00:21
@CLAassistant
Copy link

CLAassistant commented Dec 28, 2025

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 28, 2025

Walkthrough

Adds persisted, mockable macOS detection and mouse-button helpers; refactors pointer handlers to emit a ContextMenuEvent for Mac Ctrl+Click (preventing click/attack), updates .gitignore layout, and adds a test asserting Mac Ctrl+Click emits ContextMenuEvent and suppresses MouseUpEvent.

Changes

Cohort / File(s) Summary
Configuration
\.gitignore
Reorganized "Folders" vs "Files"; moved/added entries for coverage/, .env*, CLAUDE.md, TODO.txt, and .DS_Store paths.
Input Handling
src/client/InputHandler.ts
Added private readonly isMacReal and public isMac() accessor; added private isMiddleMouseButton() and private isLeftMouseButton() helpers; refactored pointer handlers to use helpers; on macOS Ctrl+Click emits ContextMenuEvent and suppresses standard MouseUp/click actions; cancels ghost-structure when opening context menu.
Testing / Exports
tests/InputHandler.test.ts
Exports updated to include ContextMenuEvent and MouseUpEvent; added test "Mac Ctrl+Click Context Menu" that mocks isMac() and asserts ContextMenuEvent is emitted and MouseUpEvent is not.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Browser as DOM
  participant InputHandler
  participant EventBus
  Note over InputHandler,EventBus: Mac Ctrl+Click flow (changed)
  User->>Browser: Ctrl+click (trackpad)
  Browser->>InputHandler: pointerup (button, modifiers, coords)
  alt InputHandler.isMac() && Control pressed
    InputHandler->>EventBus: emit ContextMenuEvent(x,y)
    Note right of EventBus: MouseUpEvent NOT emitted (no attack/click)
  else normal click / other platforms
    InputHandler->>EventBus: emit MouseUpEvent(x,y)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

Bug Fix

Suggested reviewers

  • evanpelle

Poem

🖱️ On Mac a gentle ctrl‑tap sings,
Menus open safe — no accidental stings.
Ghosts step back, handlers stay neat,
Tests confirm the quiet defeat.
Small change, calm UI brings.

Pre-merge checks

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the two main changes: fixing Ctrl+Click behavior for Mac users and reorganizing the gitignore file.
Description check ✅ Passed The description is well-structured and directly related to the changeset, explaining the problem solved, implementation details, and verification methods.
Linked Issues check ✅ Passed The pull request successfully addresses all requirements from issue #2719: prevents Ctrl+Click attacks on Mac, preserves Windows/Linux behavior, and includes proper test coverage.
Out of Scope Changes check ✅ Passed The gitignore reorganization is a minor housekeeping task that doesn't impact functionality, while all code changes directly address the linked issue requirements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e4a1a3f and 5119190.

📒 Files selected for processing (2)
  • src/client/InputHandler.ts
  • tests/InputHandler.test.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-18T17:54:01.311Z
Learnt from: sambokai
Repo: openfrontio/OpenFrontIO PR: 2225
File: src/core/execution/FakeHumanExecution.ts:172-173
Timestamp: 2025-10-18T17:54:01.311Z
Learning: In src/core/execution/FakeHumanExecution.ts, MIRVs and ground attacks should not be mutually exclusive. The considerMIRV() method should not short-circuit maybeAttack() - both actions can occur in the same tick.

Applied to files:

  • src/client/InputHandler.ts
📚 Learning: 2025-11-07T00:23:48.567Z
Learnt from: VariableVince
Repo: openfrontio/OpenFrontIO PR: 2408
File: src/client/InputHandler.ts:485-485
Timestamp: 2025-11-07T00:23:48.567Z
Learning: In JavaScript/TypeScript PointerEvent and MouseEvent, the properties event.x and event.y are aliases for event.clientX and event.clientY. They represent the same viewport-relative coordinates and can be used interchangeably.

Applied to files:

  • src/client/InputHandler.ts
  • tests/InputHandler.test.ts
🧬 Code graph analysis (1)
tests/InputHandler.test.ts (1)
src/client/InputHandler.ts (2)
  • MouseUpEvent (8-13)
  • ContextMenuEvent (52-57)
🔇 Additional comments (10)
tests/InputHandler.test.ts (2)

1-6: Clean imports for the new test.

The additions of ContextMenuEvent and MouseUpEvent are necessary for the new Mac Ctrl+Click test and properly expose the relevant event types.


461-501: Excellent test coverage for Mac Ctrl+Click behavior.

The test correctly:

  • Mocks isMac() to simulate macOS environment
  • Simulates ControlLeft being held in activeKeys
  • Verifies ContextMenuEvent is emitted instead of MouseUpEvent
  • Confirms the fix prevents accidental attacks when opening context menu

This directly validates the PR objective.

src/client/InputHandler.ts (8)

156-160: Smart encapsulation for testability.

The isMac() method wraps the platform detection in a mockable interface while keeping the actual detection logic private and immutable. Clean approach.


208-209: Good platform-aware default.

Using MetaLeft (Cmd) on Mac and ControlLeft elsewhere follows platform conventions and improves UX for Mac users.


507-515: Core fix correctly prevents Mac Ctrl+Click attacks.

This logic successfully addresses the PR objective:

  • Detects Mac platform and ControlLeft held
  • Emits ContextMenuEvent to open context menu
  • Returns early to prevent MouseUpEvent (line 529) that would trigger attack

The clear comment explains the rationale. Well done.


665-671: Clean helper methods improve readability.

These simple helpers encapsulate the button number checks (0 for left, 1 for middle) and make the pointer handling code more readable throughout the file.


456-465: Clean pointer button handling.

The use of helper methods with clarifying comments makes the button logic clear:

  • Middle mouse: trigger auto-upgrade
  • Non-left buttons: ignore

485-493: Consistent button filtering in pointer up.

The button handling mirrors the onPointerDown logic, maintaining consistency across the pointer event lifecycle.


555-562: Consistent helper usage in pointer move.

The pointer move handler correctly uses the button helpers to filter and handle hover effects only for left mouse button.


596-600: Smart UX: right-click cancels structure placement.

When a ghost structure is active, right-click now cancels the placement instead of opening the context menu. This is intuitive and prevents the confusing scenario of having both a context menu and a ghost structure.


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

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7284ded and 4fcd6a0.

📒 Files selected for processing (3)
  • .gitignore
  • src/client/InputHandler.ts
  • tests/InputHandler.test.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-07T00:23:48.567Z
Learnt from: VariableVince
Repo: openfrontio/OpenFrontIO PR: 2408
File: src/client/InputHandler.ts:485-485
Timestamp: 2025-11-07T00:23:48.567Z
Learning: In JavaScript/TypeScript PointerEvent and MouseEvent, the properties event.x and event.y are aliases for event.clientX and event.clientY. They represent the same viewport-relative coordinates and can be used interchangeably.

Applied to files:

  • src/client/InputHandler.ts
  • tests/InputHandler.test.ts
🧬 Code graph analysis (1)
tests/InputHandler.test.ts (1)
src/client/InputHandler.ts (2)
  • MouseUpEvent (8-13)
  • ContextMenuEvent (52-57)
🔇 Additional comments (11)
.gitignore (1)

1-17: Clean reorganization improves maintainability.

The alphabetized sections make the gitignore easier to scan and maintain.

tests/InputHandler.test.ts (2)

4-9: LGTM!

The additional imports support the new Mac Ctrl+Click test case.


464-504: Well-structured test validates the Mac Ctrl+Click fix.

The test correctly simulates the scenario and verifies that ContextMenuEvent is emitted instead of MouseUpEvent, preventing unintended attacks.

src/client/InputHandler.ts (8)

154-158: Good testable pattern for platform detection.

The readonly field with a mockable method enables reliable unit testing while keeping the detection simple.


206-207: Consistent use of the new isMac() method.


455-465: Helper methods improve code clarity.

Extracting button checks into named methods makes the intent clear and centralizes the logic.


484-493: LGTM!

Consistent button handling with clear comments.


521-526: Helpful comment explains touch event handling.


555-562: Consistent button filtering.


596-601: Comment clarifies ghost structure behavior.

Note: When Mac Ctrl+Click occurs, both line 513 and line 601 might emit ContextMenuEvent depending on browser behavior. This is likely fine since the UI should handle duplicate events gracefully, but worth being aware of.


649-655: Clean helper methods centralize button logic.

Simple, focused methods that make the code easier to read and maintain.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 28, 2025
@iiamlewis iiamlewis added the Bug label Dec 28, 2025
@iiamlewis iiamlewis moved this from Triage to Final Review in OpenFront Release Management Dec 28, 2025
@iiamlewis iiamlewis added this to the v29 milestone Dec 28, 2025
@iiamlewis
Copy link
Contributor

@clounie can you resolve the branch conflicts and I'll look and see what I can do about getting this reviewed!

Copy link
Contributor

@DevelopingTom DevelopingTom left a comment

Choose a reason for hiding this comment

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

Looks good to me but can't try it without a Mac.

…cCtrlClick

# Conflicts:
#	.gitignore
#	src/client/InputHandler.ts
#	tests/InputHandler.test.ts
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: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4fcd6a0 and e4a1a3f.

📒 Files selected for processing (3)
  • .gitignore
  • src/client/InputHandler.ts
  • tests/InputHandler.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • .gitignore
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-18T17:54:01.311Z
Learnt from: sambokai
Repo: openfrontio/OpenFrontIO PR: 2225
File: src/core/execution/FakeHumanExecution.ts:172-173
Timestamp: 2025-10-18T17:54:01.311Z
Learning: In src/core/execution/FakeHumanExecution.ts, MIRVs and ground attacks should not be mutually exclusive. The considerMIRV() method should not short-circuit maybeAttack() - both actions can occur in the same tick.

Applied to files:

  • src/client/InputHandler.ts
📚 Learning: 2025-11-07T00:23:48.567Z
Learnt from: VariableVince
Repo: openfrontio/OpenFrontIO PR: 2408
File: src/client/InputHandler.ts:485-485
Timestamp: 2025-11-07T00:23:48.567Z
Learning: In JavaScript/TypeScript PointerEvent and MouseEvent, the properties event.x and event.y are aliases for event.clientX and event.clientY. They represent the same viewport-relative coordinates and can be used interchangeably.

Applied to files:

  • src/client/InputHandler.ts
  • tests/InputHandler.test.ts
🧬 Code graph analysis (1)
tests/InputHandler.test.ts (1)
src/client/InputHandler.ts (2)
  • MouseUpEvent (8-13)
  • ContextMenuEvent (52-57)
🪛 GitHub Actions: 🧪 CI
tests/InputHandler.test.ts

[error] 467-467: TS2708 Cannot use namespace 'jest' as a value.

🪛 GitHub Check: 🔬 Test
tests/InputHandler.test.ts

[failure] 467-467: tests/InputHandler.test.ts > InputHandler AutoUpgrade > Mac Ctrl+Click Context Menu > should create context menu with Ctrl+Click on Mac, but not attack
ReferenceError: jest is not defined
❯ tests/InputHandler.test.ts:467:7

🔇 Additional comments (7)
tests/InputHandler.test.ts (1)

464-504: Test logic looks correct.

Once the jestvi issue is fixed, this test properly verifies that Mac Ctrl+Click emits ContextMenuEvent instead of MouseUpEvent, preventing accidental attacks on macOS.

src/client/InputHandler.ts (6)

156-160: LGTM! Clean Mac detection with test support.

The readonly field plus public method pattern makes testing straightforward while keeping the detection logic simple.


665-671: LGTM! Helper methods clarify mouse button logic.

These methods replace magic numbers with clear intent. button === 1 for middle-click and button > 0 for non-left buttons are both correct.


507-515: LGTM! Mac Ctrl+Click fix prevents accidental attacks.

The logic correctly emits ContextMenuEvent and returns early to suppress MouseUpEvent (which would trigger an attack). This resolves the reported issue.


454-465: LGTM! Clean middle-click and button filtering.

The helper methods make the intent clear: middle-click triggers auto-upgrade, and non-left buttons are filtered early.


596-600: LGTM! Right-click cancels ghost structure placement.

This provides clear feedback: when placing a structure, right-click exits placement mode instead of opening the context menu. Good UX.


208-209: LGTM! Platform-aware modifier key selection.

Mac users get Cmd (MetaLeft) as the modifier, while others get Ctrl (ControlLeft). This aligns with platform conventions.

@github-project-automation github-project-automation bot moved this from Final Review to Development in OpenFront Release Management Dec 30, 2025
@clounie
Copy link
Contributor Author

clounie commented Dec 30, 2025

@iiamlewis should be good to go -

  • Resolved conflicts
  • Adopted vitest for my tests
  • Re-ran format/lint/test, everything passed
  • Re-tested in UI, Ctrl+Click and Cmd+Click both still work as expected after update
    • Ctrl+Click does not attack
    • Cmd+Click behavior unchanged

Copy link
Contributor

@DevelopingTom DevelopingTom left a comment

Choose a reason for hiding this comment

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

Thanks!

@github-project-automation github-project-automation bot moved this from Development to Final Review in OpenFront Release Management Dec 30, 2025
@DevelopingTom DevelopingTom added this pull request to the merge queue Dec 31, 2025
Merged via the queue into openfrontio:main with commit 40eea22 Dec 31, 2025
7 checks passed
@github-project-automation github-project-automation bot moved this from Final Review to Complete in OpenFront Release Management Dec 31, 2025
ryanbarlow97 added a commit that referenced this pull request Dec 31, 2025
evanpelle added a commit that referenced this pull request Dec 31, 2025
This reverts commit 40eea22.

Commit breaks map dragging ability.
@ryanbarlow97
Copy link
Contributor

@clounie this broke non mac pcs

@clounie
Copy link
Contributor Author

clounie commented Dec 31, 2025

@clounie this broke non mac pcs

I'll have to debug. At first glance this seems to imply the isMac boolean must be broken in some way, as-is. Or that moving it to instance state broke it..

For future me - "Commit breaks map dragging ability."

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

Labels

Projects

Status: Complete

Development

Successfully merging this pull request may close these issues.

Ctrl+Click on Mac opens radial menu, but *also* performs an attack

5 participants