Skip to content

fix(v2): ignore phantom/non-standard gamepads in nav polling#3852

Merged
gantoine merged 3 commits into
masterfrom
fix/gamepad-phantom-pad-nav-loop
Jul 20, 2026
Merged

fix(v2): ignore phantom/non-standard gamepads in nav polling#3852
gantoine merged 3 commits into
masterfrom
fix/gamepad-phantom-pad-nav-loop

Conversation

@gantoine

Copy link
Copy Markdown
Member

Description
Explain the changes or enhancements you are proposing with this pull request.

Fixes #3851 — the v2 UI would enter an infinite loop cycling through the top-level pages (home → platforms → collections → search) right after login, making the app unusable. Reported in Firefox/Zen with no controller in use.

Root cause

The v2 gamepad polling loop (frontend/src/v2/composables/useGamepad/index.ts) reads buttons and axes by hardcoded W3C standard-mapping indices, but only guarded against null pads (if (!pad) continue;). Firefox (and Zen) expose phantom or non-standard-mapping gamepads even when no real controller is attached, and their analog controls drift across the digital press threshold. Since buttons 4/5 (LB/RB) are bound to AppNav section cycling, each false → true oscillation re-fired cycleSection(), walking the router through the four sections cyclically with zero user input. detectPadPresence() also flipped the app into "pad" input modality for these phantom devices.

The fix

Added an isUsablePad() guard requiring pad.connected && pad.mapping === "standard", applied in both the polling loop and detectPadPresence(). Every button/axis read assumes the standard mapping, so a non-standard pad cannot be interpreted safely regardless, and skipping it is the correct behavior. The controller-debug screen polls navigator.getGamepads() directly, so it still displays non-standard pads for inspection.

AI assistance disclosure

This change was written with AI assistance (Claude Code): investigation of the root cause and the code change were AI-generated and human-reviewed.

Checklist
Please check all that apply.

  • I've tested the changes locally (typecheck + lint pass)
  • I've updated relevant comments
  • I've assigned reviewers for this PR
  • I've added unit tests that cover the changes

Screenshots (if applicable)

N/A — input-guard fix. Reproduction video is in #3851.

🤖 Generated with Claude Code

The v2 gamepad polling loop reads buttons and axes by hardcoded W3C
standard-mapping indices, but only guarded against null pads. Firefox
(and Zen) expose phantom or non-standard-mapping gamepads even with no
real controller attached, whose analog controls drift across the press
threshold. Index 4/5 (LB/RB) are bound to AppNav section cycling, so
each oscillation re-fired cycleSection and looped the app through
home/platforms/collections/search with no user input, making the UI
unusable.

Add an isUsablePad() guard requiring pad.connected and a "standard"
mapping, applied in both the polling loop and detectPadPresence. Every
button/axis read assumes the standard mapping, so a non-standard pad
cannot be interpreted safely regardless.

Fixes #3851

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 20, 2026 14:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@greptile-apps

greptile-apps Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR filters unusable gamepads from v2 navigation polling. The main changes are:

  • Adds a shared connected and standard-mapping check.
  • Applies the check to input-modality detection.
  • Skips phantom and non-standard devices during navigation polling.

Confidence Score: 4/5

The strict mapping filter can disable legitimate connected controllers and should be addressed before merging.

  • Phantom and disconnected devices are filtered from both relevant paths.
  • Connected controllers that report an empty mapping are also rejected before any input is read.
  • Affected controllers remain visible in the debug screen but cannot navigate the application.

frontend/src/v2/composables/useGamepad/index.ts

Important Files Changed

Filename Overview
frontend/src/v2/composables/useGamepad/index.ts Adds a shared gamepad guard, but the strict mapping check can exclude legitimate connected controllers.

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
frontend/src/v2/composables/useGamepad/index.ts:114
**Connected Controllers Are Silently Ignored**

A legitimate controller can be connected while reporting an empty mapping, as occurs with some Switch Pro, Joy-Con, and 8BitDo browser/OS combinations. The new equality check rejects every such device before polling, so navigation stops working even when its buttons previously matched the expected indices; the controller-debug screen still shows the device, which makes this failure appear inconsistent.

Reviews (1): Last reviewed commit: "fix(v2): ignore phantom/non-standard gam..." | Re-trigger Greptile

Comment thread frontend/src/v2/composables/useGamepad/index.ts Outdated
gantoine and others added 2 commits July 20, 2026 11:02
Requiring mapping === "standard" rejected legitimate controllers that
report an empty mapping (some Switch Pro, Joy-Con, 8BitDo browser/OS
combos) while still exposing the standard button indices, breaking
navigation for them. There are only two mapping values, so the check
dropped every empty-mapping device. Gate on pad.connected alone, which
is the actual discriminator for the disconnected phantom entries that
caused #3851.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gantoine
gantoine merged commit 8ddcf01 into master Jul 20, 2026
11 checks passed
@gantoine
gantoine deleted the fix/gamepad-phantom-pad-nav-loop branch July 20, 2026 15:18
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.

[Bug] New UI repeatedly loops through pages, app unusable

2 participants