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

7.7 KiB

Overlay Controller Support Implementation Plan

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Add Chrome Gamepad API controller support to the visible overlay as a supplement to keyboard-only mode, including controller selection/debug modals, config-backed logical bindings, and selected-token highlight cleanup.

Architecture: Keep controller support in the visible overlay renderer. Poll and normalize gamepad state in a dedicated runtime, route logical actions into the existing keyboard-only/Yomitan helpers, and persist preferred-controller config through the existing config pipeline and preload bridge.

Tech Stack: TypeScript, Bun tests, Electron preload IPC, renderer DOM modals, Chrome Gamepad API


Task 1: Track work and lock the design

Files:

  • Create: backlog/tasks/task-159 - Add-overlay-controller-support-for-keyboard-only-mode.md
  • Create: docs/plans/2026-03-11-overlay-controller-support-design.md
  • Create: docs/plans/2026-03-11-overlay-controller-support.md

Step 1: Record the approved scope

Capture controller-only-in-keyboard-mode behavior, the modal shortcuts, config scope, and the stale selection-highlight cleanup requirement.

Step 2: Verify the written scope matches the approved design

Run: sed -n '1,220p' backlog/tasks/task-159\\ -\\ Add-overlay-controller-support-for-keyboard-only-mode.md && sed -n '1,240p' docs/plans/2026-03-11-overlay-controller-support-design.md

Expected: task and design doc both mention controller selection/debug modals and highlight cleanup.

Task 2: Add failing config tests and defaults

Files:

  • Modify: src/config/config.test.ts
  • Modify: src/config/definitions/defaults-core.ts
  • Modify: src/config/definitions/options-core.ts
  • Modify: src/config/definitions/template-sections.ts
  • Modify: src/types.ts
  • Modify: config.example.jsonc

Step 1: Write the failing test

Add coverage asserting a new controller config block resolves with the expected defaults and accepts logical-field overrides.

Step 2: Run test to verify it fails

Run: bun test src/config/config.test.ts

Expected: FAIL because controller config is not defined yet.

Step 3: Write minimal implementation

Add the controller config types/defaults/registry/template wiring and regenerate the example config if needed.

Step 4: Run test to verify it passes

Run: bun test src/config/config.test.ts

Expected: PASS

Task 3: Add failing keyboard-selection cleanup tests

Files:

  • Modify: src/renderer/handlers/keyboard.test.ts
  • Modify: src/renderer/handlers/keyboard.ts
  • Modify: src/renderer/state.ts

Step 1: Write the failing tests

Add tests for:

  • turning keyboard-only mode off clears .keyboard-selected
  • closing the popup clears stale selection highlight when keyboard-only mode is off

Step 2: Run test to verify it fails

Run: bun test src/renderer/handlers/keyboard.test.ts

Expected: FAIL because selection cleanup is incomplete today.

Step 3: Write minimal implementation

Centralize selection clearing in the keyboard-only sync helpers and popup-close flow.

Step 4: Run test to verify it passes

Run: bun test src/renderer/handlers/keyboard.test.ts

Expected: PASS

Task 4: Add failing controller runtime tests

Files:

  • Create: src/renderer/handlers/gamepad-controller.test.ts
  • Create: src/renderer/handlers/gamepad-controller.ts
  • Modify: src/renderer/context.ts
  • Modify: src/renderer/state.ts
  • Modify: src/renderer/renderer.ts

Step 1: Write the failing tests

Cover:

  • first connected controller is selected by default
  • preferred controller wins when connected
  • controller actions are ignored unless keyboard-only mode is enabled, except keyboard-only toggle
  • stick/button mappings invoke the expected logical helpers
  • smooth scroll and repeat throttling behavior

Step 2: Run test to verify it fails

Run: bun test src/renderer/handlers/gamepad-controller.test.ts

Expected: FAIL because controller runtime does not exist.

Step 3: Write minimal implementation

Add a renderer-local polling runtime with deadzone handling, action edge detection, repeat timing, and helper callbacks into the keyboard/Yomitan flow.

Step 4: Run test to verify it passes

Run: bun test src/renderer/handlers/gamepad-controller.test.ts

Expected: PASS

Task 5: Add failing controller modal tests

Files:

  • Modify: src/renderer/index.html
  • Modify: src/renderer/style.css
  • Create: src/renderer/modals/controller-select.ts
  • Create: src/renderer/modals/controller-select.test.ts
  • Create: src/renderer/modals/controller-debug.ts
  • Create: src/renderer/modals/controller-debug.test.ts
  • Modify: src/renderer/renderer.ts
  • Modify: src/renderer/context.ts
  • Modify: src/renderer/state.ts

Step 1: Write the failing tests

Add tests for:

  • Alt+C opens controller selection modal
  • Alt+Shift+C opens controller debug modal
  • selection modal renders connected controllers and persists the chosen device
  • debug modal shows live axes/buttons state

Step 2: Run test to verify it fails

Run: bun test src/renderer/modals/controller-select.test.ts src/renderer/modals/controller-debug.test.ts

Expected: FAIL because modals and shortcuts do not exist.

Step 3: Write minimal implementation

Add modal DOM, renderer modules, modal state wiring, and controller runtime integration.

Step 4: Run test to verify it passes

Run: bun test src/renderer/modals/controller-select.test.ts src/renderer/modals/controller-debug.test.ts

Expected: PASS

Task 6: Persist controller preference through preload/main wiring

Files:

  • Modify: src/preload.ts
  • Modify: src/types.ts
  • Modify: src/shared/ipc/contracts.ts
  • Modify: src/core/services/ipc.ts
  • Modify: src/main.ts
  • Modify: related main/runtime tests as needed

Step 1: Write the failing test

Add coverage for reading current controller config and saving preferred-controller changes from the renderer.

Step 2: Run test to verify it fails

Run: bun test src/core/services/ipc.test.ts

Expected: FAIL because no controller preference IPC exists yet.

Step 3: Write minimal implementation

Expose renderer-safe getters/setters for the controller config fields needed by the selection modal/runtime.

Step 4: Run test to verify it passes

Run: bun test src/core/services/ipc.test.ts

Expected: PASS

Task 7: Update docs and config example

Files:

  • Modify: config.example.jsonc
  • Modify: README.md
  • Modify: relevant docs under docs-site/ for shortcuts/usage/troubleshooting if touched by current docs structure

Step 1: Write the failing doc/config check if needed

If config example generation is covered by tests, add/refresh the failing assertion first.

Step 2: Implement the docs

Document controller behavior, modal shortcuts, config block, and the keyboard-only-only activation rule.

Step 3: Run doc/config verification

Run: bun run test:config

Expected: PASS

Task 8: Run the handoff gate and update the backlog task

Files:

  • Modify: backlog/tasks/task-159 - Add-overlay-controller-support-for-keyboard-only-mode.md

Step 1: Run targeted verification

Run:

  • bun test src/config/config.test.ts
  • bun test src/renderer/handlers/keyboard.test.ts
  • bun test src/renderer/handlers/gamepad-controller.test.ts
  • bun test src/renderer/modals/controller-select.test.ts
  • bun test src/renderer/modals/controller-debug.test.ts
  • bun test src/core/services/ipc.test.ts

Expected: PASS

Step 2: Run broader gate

Run:

  • bun run typecheck
  • bun run test:fast
  • bun run test:env
  • bun run build

Expected: PASS, or document exact blockers/failures.

Step 3: Update backlog notes

Fill in implementation notes, verification commands, and final summary in TASK-159.