Skip to content

Developer Setup

mojouto3 edited this page Aug 1, 2026 · 1 revision

Developer Setup

If you want to run or modify Mojo Input Manager from source, follow these steps.


Requirements

  • Node.js
  • npm (included with Node.js)
  • Windows (the app, and the vJoy/HidHide drivers it automates, are Windows-only)
  • vJoy and HidHide installed, if you want to exercise those features rather than just the UI

Steps

1. Clone the repository

git clone https://github.com/mojouto3/mojo-input-manager.git
cd mojo-input-manager

2. Install dependencies

npm install

3. Start the app in development mode

npm run dev

This runs Vite (renderer, with hot module reload) and Electron together. DevTools open automatically in dev mode.

Note: Files with a requestAnimationFrame loop, currently Mapping.jsx, have occasionally shown stale-closure symptoms under hot reload (old code keeps running after a save). If Mapping behaves unexpectedly after an edit, do a full restart (stop the dev server, kill any lingering electron.exe, run npm run dev again) rather than trusting HMR.

4. Build a production installer (optional)

npm run dist

Builds the renderer and packages a Windows NSIS installer into release/. Use npm run dist:dir for an unpacked build (faster, useful for quick manual testing without the installer step).

5. Publish a release (maintainers only)

npm run dist -- --publish=always

Requires a GH_TOKEN environment variable with permission to upload release assets. This builds, then uploads the installer and update metadata (latest.yml, block map) to a GitHub Release, which is what the auto-updater checks against.


Project Structure

mojo-input-manager/
├── assets/                    Icons, logos, social preview
├── src/
│   ├── main/
│   │   ├── main.js             Electron main process: window, tray, IPC, auto-updater
│   │   ├── vjoy.js              vJoyConfig.exe wrapper (create/delete, elevated)
│   │   ├── vjoyInterface.js     vJoyInterface.dll wrapper via koffi (live feed)
│   │   ├── hidhide.js           HidHideCLI.exe wrapper (devices, cloak, apps)
│   │   ├── profiles.js          Per-game device filtering profiles
│   │   └── mappingProfiles.js   Remembered physical-device-to-vJoy mappings
│   └── renderer/
│       ├── preload.js            contextBridge: the only surface the renderer can reach main through
│       ├── components/           Shared UI: Card, Button, Badge, Toggle, Select, Sidebar, TitleBar...
│       ├── theme.js               Accent theme (green/cyan) persistence
│       └── views/                 Dashboard, VirtualDevices, Mapping, DeviceFiltering, Settings
└── package.json

Notable implementation details:

  • vJoy: vjoy.js shells out to vJoyConfig.exe for create/delete (elevated via a UAC prompt, since those change persistent driver config), while vjoyInterface.js calls vJoyInterface.dll directly through koffi (FFI) for the actual live axis/button feed, which doesn't need elevation.
  • HidHide: hidhide.js spawns HidHideCLI.exe with stdio: ['ignore', 'pipe', 'pipe'] rather than using execFile, because the CLI blocks waiting on stdin otherwise. Calls are also serialized through an internal queue, since the CLI doesn't tolerate concurrent invocations.
  • Physical device detection: done entirely through the browser's Gamepad API in the renderer, no native module needed just to see connected controllers.
  • Mapping: a requestAnimationFrame loop in Mapping.jsx polls navigator.getGamepads() and sends combined axis/button state to the main process over IPC, which feeds it into vJoy via vjoyInterface.js.

Git Workflow

This project uses a two-account workflow:

Account Role
mojouto3 Manager: repo administration, releases, milestones
Constantinos-T Developer: feature branches, commits, opens PRs

Both open PRs for their own contributions; the other reviews before merge (GitHub doesn't allow self-approval). All PRs target main and require 1 review. Branches are deleted after merging.

git checkout main
git pull origin main
git checkout -b your-feature-name

Notes

  • The dist/ (renderer build output) and release/ (packaged installer) folders are gitignored, safe to delete, and recreated on every build.
  • The auto-updater only runs in packaged builds (app.isPackaged). In dev mode it's a no-op, and the Settings tab reports "Only available in installed builds".

Clone this wiki locally