Files
SubMiner/docs/plans/2026-03-11-overlay-controller-support-design.md

4.4 KiB

Overlay Controller Support Design

Date: 2026-03-11 Backlog: TASK-159

Goal

Add controller support to the visible overlay through the Chrome Gamepad API without replacing the existing keyboard-only workflow. Controller input should only supplement keyboard-only mode, preserve existing behavior, and expose controller selection plus raw-input debugging in overlay-local modals.

Scope

  • Poll connected gamepads from the visible overlay renderer.
  • Default to the first connected controller unless config specifies a preferred controller.
  • Add logical controller bindings and tuning knobs to config.
  • Add Alt+C controller selection modal.
  • Add Alt+Shift+C controller debug modal.
  • Map controller actions onto existing keyboard-only/Yomitan behaviors.
  • Fix stale selected-token highlight cleanup when keyboard-only mode turns off or popup closes.

Out of scope for this pass:

  • Raw arbitrary axis/button index remapping in config.
  • Controller support outside the visible overlay renderer.
  • Haptics or vibration.

Architecture

Use a renderer-local controller runtime. The overlay already owns keyboard-only token selection, Yomitan popup integration, and modal UX, and the Gamepad API is browser-native. A renderer module can poll navigator.getGamepads() on animation frames, normalize sticks/buttons into logical actions, and call the same helpers used by keyboard-only mode.

Avoid synthetic keyboard events as the primary implementation. Analog sticks need deadzones, continuous smooth scrolling, and per-action repeat behavior that do not fit cleanly into key event emulation. Direct logical actions keep tests clear and make the debug modal show the exact values the runtime uses.

Behavior

Controller actions are active only while keyboard-only mode is enabled, except the controller action that toggles keyboard-only mode can always fire so the user can enter the mode from the controller.

Default logical mappings:

  • left stick vertical: smooth Yomitan popup/window scroll when popup is open
  • left stick horizontal: move token selection left/right
  • right stick vertical: smooth Yomitan popup/window scroll
  • right stick horizontal: jump horizontally inside Yomitan popup/window
  • A: toggle lookup
  • B: close lookup
  • Y: toggle keyboard-only mode
  • X: mine card
  • L1 / R1: previous / next Yomitan audio
  • R2: activate current Yomitan audio button
  • L2: toggle mpv play/pause

Selection-highlight cleanup:

  • disabling keyboard-only mode clears the selected token class immediately
  • closing the Yomitan popup also clears the selected token class if keyboard-only mode is no longer active
  • helper ownership should live in the shared keyboard-only selection sync path so keyboard and controller exits stay consistent

Config

Add a top-level controller block in resolved config with:

  • enabled
  • preferredGamepadId
  • preferredGamepadLabel
  • smoothScroll
  • scrollPixelsPerSecond
  • horizontalJumpPixels
  • stickDeadzone
  • triggerDeadzone
  • repeatDelayMs
  • repeatIntervalMs
  • bindings logical fields for the named actions/sticks

Persist the preferred controller by stable browser-exposed id when possible, with label stored as a diagnostic/display fallback.

UI

Controller selection modal:

  • overlay-hosted modal in the visible renderer
  • lists currently connected controllers
  • highlights current active choice
  • selecting one persists config and makes it the active controller immediately if connected

Controller debug modal:

  • overlay-hosted modal
  • shows selected controller and all connected controllers
  • live raw axis array values
  • live raw button values, pressed flags, and touched flags if available

Testing

Test first:

  • controller gating outside keyboard-only mode
  • logical mapping to existing helpers
  • continuous stick scroll and repeat behavior
  • modal open shortcuts
  • preferred-controller selection persistence
  • highlight cleanup on keyboard-only disable and popup close
  • config defaults/parse/template generation coverage

Risks

  • Browser gamepad identity strings can differ across OS/browser/runtime versions. Mitigation: match by exact preferred id first; fall back to first connected controller.
  • Continuous stick input can spam actions. Mitigation: deadzones plus repeat throttling and frame-time-based smooth scroll.
  • Popup DOM/audio controls may vary. Mitigation: target stable Yomitan popup/document selectors and cover with focused renderer tests.