Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
name: Feature request
about: Suggest an improvement to InputPilot
title: ""
labels: enhancement
---

## What problem would this solve?

<!-- Describe the situation you run into, not just the feature. What are you doing when it bites? -->

## What would you expect to happen?

## Your setup

- macOS version:
- Keyboards involved (model, USB/Bluetooth/built-in):
- Input sources you switch between:

## Anything you have tried

<!-- Existing options that came close: per-device fallbacks, global fallback, pause, the modifier-only toggle. -->
45 changes: 45 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Changelog

All notable changes to InputPilot are documented here. This project follows [Semantic Versioning](https://semver.org).

## [1.0.1] — 2026-08-18

### Fixed

- **First launch now asks for Input Monitoring.** 1.0.0 only *checked* the permission and never requested it, so a new user saw nothing at all: no prompt, no window, and — because macOS does not list an app that has never requested access — no way to grant it by hand either. A welcome window now explains what InputPilot does, why the permission is needed, and that it never reads typed text, with a button that triggers the system prompt.
- The welcome window also covers the state right after a grant, where macOS reports the permission as granted but only applies it on the next launch, by offering to quit and reopen instead of leaving the app looking permitted but inert.
- "Fix in Settings…" in the conflict warning did nothing. It used `SettingsLink`, which targets a `Settings` scene the app does not declare.

### Changed

- The release pipeline now derives versions from the project file and refuses to build when the marketing version does not match the requested release, or when the build number is not greater than the one the published appcast advertises. Sparkle compares build numbers, so the previous behaviour could have shipped an update that was never offered.

## [1.0.0] — 2026-08-18

First public release.

### Added

- Per-keyboard input source mapping, with per-device and global fallbacks.
- Undo for the last automatic switch, and pause for 15 or 60 minutes.
- Conflict detection for mappings pointing at removed or disabled input sources.
- Debounce (400 ms) and cooldown (1.5 s) to prevent flapping between keyboards.
- Optional switching on modifier-only key presses, off by default, so ⌘-Tab does not change the layout.
- In-app debug log with copy and export, containing device and status information only.
- Automatic updates via Sparkle, over a signed appcast.

### Fixed

Issues found in the pre-release audit and fixed before shipping:

- Switch decisions and undo read the current input source fresh instead of a cache that could be six seconds stale, so an input source changed outside the app no longer suppresses a switch or corrupts what undo restores.
- Undecodable mapping data is preserved under a `.corrupted` backup key and logged, instead of being silently replaced on the next write — previously one decode failure could destroy every stored mapping.
- Keyboards without vendor or product IDs are no longer treated as one shared device.
- Unplugging the active keyboard now clears it from the display.

### Security

- Signed with a Developer ID certificate and notarized by Apple; both the app and the DMG carry a stapled notarization ticket, so first launch works offline.

[1.0.1]: https://github.com/LucaGerlich/InputPilot/releases/tag/v1.0.1
[1.0.0]: https://github.com/LucaGerlich/InputPilot/releases/tag/v1.0.0
38 changes: 38 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Contributing

Thanks for taking an interest in InputPilot. Bug reports, ideas, and pull requests are all welcome.

## Reporting bugs

Open an issue with the [bug report template](https://github.com/LucaGerlich/InputPilot/issues/new/choose). The single most useful thing you can attach is an exported debug log: menu bar icon → **Open Debug** → reproduce the problem → **Export…**. It contains device and status information only, never anything you typed.

Please include your macOS version, the InputPilot version (Settings → About), and which keyboards are involved — device-identity behaviour varies a lot between USB, Bluetooth, and built-in keyboards.

## Development setup

You need **Xcode 26** or newer.

```bash
xcodebuild -scheme InputPilot -destination 'platform=macOS' build test
```

One thing that will confuse you otherwise: an app launched from Xcode runs under the debugger, and macOS attributes Input Monitoring to **Xcode**, not to InputPilot. Anything touching the permission flow has to be tested with a build launched from Finder.

## Pull requests

- Branch from `main`, keep commits small and coherent, and use `feat:` / `fix:` / `docs:` / `chore:` prefixes.
- CI runs build and tests on every pull request; it must be green.
- Add tests for logic changes. `AppState` and the services are protocol-injected precisely so behaviour can be tested without HID hardware — see `InputPilotTests/ServiceMocks.swift`.
- Some areas cannot be covered by automated tests: the permission flow, the release pipeline, and anything TCC-related. If you touch those, say in the pull request how you verified it manually.

## Things worth knowing before you change them

**Device identity.** Keyboards are matched on vendor, product, transport, built-in status, and normalized product name, with location as a tie-breaker. `KeyboardDeviceKey.id` is the key under which mappings are persisted, so changing how it is composed silently breaks every existing user's configuration.

**Switch timing.** Debounce and cooldown live in `SwitchController` behind an injectable clock. Changes there should come with tests using `ControlledClock` rather than sleeps.

**Privacy.** The HID callback deliberately extracts only the device and a modifier flag. Please keep it that way; see [SECURITY.md](SECURITY.md).

## Releases

Releases are cut by the maintainer with `Scripts/release.sh`, which requires a Developer ID certificate, notarytool credentials, and the Sparkle signing key. Contributors do not need any of that.
Loading