rethocker 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -53
- package/package.json +1 -1
- package/src/assets/logo.png +0 -0
- package/src/cli.ts +39 -21
package/README.md
CHANGED
|
@@ -1,8 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="src/assets/logo.png" width="280px" align="center" alt="rethocker logo" />
|
|
3
|
+
<h1 align="center">rethocker</h1>
|
|
4
|
+
<p align="center">
|
|
5
|
+
System-wide key interception and remapping for macOS, in TypeScript.
|
|
6
|
+
</p>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<!--- badges -->
|
|
10
|
+
<p align="center">
|
|
11
|
+
<a href="https://github.com/benjamine/rethocker/actions?query=branch%3Amain"><img src="https://github.com/benjamine/rethocker/actions/workflows/publish.yml/badge.svg?branch=main" alt="rethocker CI status" /></a>
|
|
12
|
+
<a href="https://twitter.com/beneidel" rel="nofollow"><img src="https://img.shields.io/badge/created%20by-@beneidel-BACABA.svg" alt="Created by Benjamin Eidelman"></a>
|
|
13
|
+
<a href="https://opensource.org/licenses/MIT" rel="nofollow"><img src="https://img.shields.io/github/license/benjamine/rethocker" alt="License"></a>
|
|
14
|
+
<a href="https://www.npmjs.com/package/rethocker" rel="nofollow"><img src="https://img.shields.io/npm/dw/rethocker.svg" alt="npm"></a>
|
|
15
|
+
<a href="https://github.com/benjamine/rethocker" rel="nofollow"><img src="https://img.shields.io/github/stars/benjamine/rethocker" alt="stars"></a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
- Requires **macOS 13+** and **Accessibility permission** (prompted automatically on first run).
|
|
21
|
+
- Built with a native daemon for low-latency key interception, and a TypeScript API for maximum flexibility and AI agent friendliness.
|
|
22
|
+
- Intercept any key or key chord, with per-app scoping and advanced conditions.
|
|
23
|
+
- Remap to other keys, execute shell commands, or call TypeScript handlers with full access to the API.
|
|
24
|
+
- Bundled with convenient actions for common macOS tasks like window management, media control, etc..
|
|
6
25
|
|
|
7
26
|
## Install
|
|
8
27
|
|
|
@@ -14,7 +33,7 @@ brew install rethocker
|
|
|
14
33
|
rethocker install
|
|
15
34
|
```
|
|
16
35
|
|
|
17
|
-
`rethocker install` scaffolds `~/.config/rethocker/default.ts` and registers a LaunchAgent that starts on login and auto-reloads whenever you save the file.
|
|
36
|
+
`rethocker install` scaffolds `~/.config/rethocker/default.ts` (where you write your own rules) and registers a LaunchAgent that starts on login and auto-reloads whenever you save the file.
|
|
18
37
|
|
|
19
38
|
```bash
|
|
20
39
|
rethocker log # live key monitor — see what rethocker captures
|
|
@@ -34,6 +53,8 @@ bun add rethocker
|
|
|
34
53
|
## Usage
|
|
35
54
|
|
|
36
55
|
```ts
|
|
56
|
+
#!/usr/bin/env bun
|
|
57
|
+
|
|
37
58
|
import { actions, Key, rethocker } from "rethocker"
|
|
38
59
|
|
|
39
60
|
const rk = rethocker([
|
|
@@ -253,6 +274,8 @@ actions.window.thirdCenter()
|
|
|
253
274
|
actions.window.thirdRight()
|
|
254
275
|
actions.window.quarterTopLeft()
|
|
255
276
|
actions.window.maximize()
|
|
277
|
+
|
|
278
|
+
// NOTE: you can use this to compose any custom layouts
|
|
256
279
|
actions.window.halfLeft("Figma") // move specific app
|
|
257
280
|
|
|
258
281
|
// App management
|
|
@@ -272,8 +295,6 @@ actions.media.volumeDown(10)
|
|
|
272
295
|
// System
|
|
273
296
|
actions.system.sleep()
|
|
274
297
|
actions.system.lockScreen()
|
|
275
|
-
actions.system.missionControl()
|
|
276
|
-
actions.system.emptyTrash()
|
|
277
298
|
|
|
278
299
|
// Run a Shortcut from the macOS Shortcuts app
|
|
279
300
|
actions.shortcut("My Shortcut Name")
|
|
@@ -365,48 +386,3 @@ await rk.stop()
|
|
|
365
386
|
// (by default rethocker keeps the event loop alive)
|
|
366
387
|
rk.unref()
|
|
367
388
|
```
|
|
368
|
-
|
|
369
|
-
## API reference
|
|
370
|
-
|
|
371
|
-
### `rethocker(rules?, options?)` → `RethockerHandle`
|
|
372
|
-
|
|
373
|
-
| Option | Type | Description |
|
|
374
|
-
|---|---|---|
|
|
375
|
-
| `binaryPath` | `string?` | Override the native binary path |
|
|
376
|
-
|
|
377
|
-
### `RethockerHandle`
|
|
378
|
-
|
|
379
|
-
| Method | Returns | Description |
|
|
380
|
-
|---|---|---|
|
|
381
|
-
| `add(rule \| rule[])` | `void` | Add one or more rules |
|
|
382
|
-
| `remove(id)` | `void` | Remove a rule permanently |
|
|
383
|
-
| `enable(id?)` | `void` | Enable a rule by ID, or all rules if no ID |
|
|
384
|
-
| `disable(id?)` | `void` | Disable a rule by ID, or all rules if no ID |
|
|
385
|
-
| `on(event, listener)` | `() => void` | Subscribe to an event; returns an unsubscribe function |
|
|
386
|
-
| `execute(command)` | `Promise<void>` | Run a shell command immediately (accepts `string \| string[]`) |
|
|
387
|
-
| `start()` | `Promise<void>` | Await daemon readiness (optional) |
|
|
388
|
-
| `stop()` | `Promise<void>` | Stop the daemon |
|
|
389
|
-
| `unref()` | `void` | Allow the process to exit while the daemon runs |
|
|
390
|
-
| `ready` | `boolean` | Whether the daemon is ready |
|
|
391
|
-
|
|
392
|
-
### Events
|
|
393
|
-
|
|
394
|
-
| Event | Arguments | Description |
|
|
395
|
-
|---|---|---|
|
|
396
|
-
| `"ready"` | — | Daemon ready |
|
|
397
|
-
| `"key"` | `KeyEvent` | Every key event (stream auto-activates on subscribe) |
|
|
398
|
-
| `"accessibilityDenied"` | — | Accessibility permission not granted |
|
|
399
|
-
| `"error"` | `code, message` | Native daemon error |
|
|
400
|
-
| `"exit"` | `code` | Native process exited unexpectedly |
|
|
401
|
-
|
|
402
|
-
### `KeyEvent`
|
|
403
|
-
|
|
404
|
-
| Field | Type | Description |
|
|
405
|
-
|---|---|---|
|
|
406
|
-
| `type` | `"keydown" \| "keyup" \| "flags"` | Event type |
|
|
407
|
-
| `keyCode` | `number` | macOS virtual key code |
|
|
408
|
-
| `modifiers` | `Modifier[]` | Active modifiers |
|
|
409
|
-
| `app` | `string?` | Frontmost app display name |
|
|
410
|
-
| `appBundleID` | `string?` | Frontmost app bundle ID |
|
|
411
|
-
| `suppressed` | `boolean` | Whether the key was consumed |
|
|
412
|
-
| `ruleID` | `string?` | ID of the matched rule |
|
package/package.json
CHANGED
|
Binary file
|
package/src/cli.ts
CHANGED
|
@@ -183,26 +183,44 @@ async function cmdRunConfig(configFile: string) {
|
|
|
183
183
|
|
|
184
184
|
// ─── Help ─────────────────────────────────────────────────────────────────────
|
|
185
185
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
186
|
+
function buildHelp() {
|
|
187
|
+
const lines = [
|
|
188
|
+
"Usage: rethocker <command>",
|
|
189
|
+
"",
|
|
190
|
+
"Commands:",
|
|
191
|
+
" install Scaffold a config file and set up a background agent that",
|
|
192
|
+
" starts on login and auto-reloads when the config is saved",
|
|
193
|
+
" uninstall Stop the background agent and remove it (keeps your config)",
|
|
194
|
+
" restart Restart the background agent",
|
|
195
|
+
" status Show whether the background agent is running",
|
|
196
|
+
" log Live key monitor — shows keypresses in rethocker rule syntax",
|
|
197
|
+
" so you can copy-paste them directly into your config",
|
|
198
|
+
" help Print this help message",
|
|
199
|
+
"",
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
if (existsSync(PLIST_FILE)) {
|
|
203
|
+
const result = run([
|
|
204
|
+
"launchctl",
|
|
205
|
+
"print",
|
|
206
|
+
`${AGENT_DOMAIN}/${AGENT_LABEL}`,
|
|
207
|
+
]);
|
|
208
|
+
const running = result.ok;
|
|
209
|
+
lines.push(
|
|
210
|
+
`Config: ${tildeify(CONFIG_FILE)} (your rethocker rules are here)`,
|
|
211
|
+
);
|
|
212
|
+
lines.push(`Status: ${running ? "running" : "not running"}`);
|
|
213
|
+
if (!running)
|
|
214
|
+
lines.push('Run "rethocker restart" to start the background agent.');
|
|
215
|
+
} else {
|
|
216
|
+
lines.push(
|
|
217
|
+
`Config: ${tildeify(CONFIG_FILE)} (not found — run "rethocker install")`,
|
|
218
|
+
);
|
|
219
|
+
}
|
|
203
220
|
|
|
204
|
-
Docs: ${GITHUB}
|
|
205
|
-
|
|
221
|
+
lines.push("", `Docs: ${GITHUB}`);
|
|
222
|
+
return lines.join("\n");
|
|
223
|
+
}
|
|
206
224
|
|
|
207
225
|
// ─── Command dispatch ─────────────────────────────────────────────────────────
|
|
208
226
|
|
|
@@ -240,12 +258,12 @@ switch (subcommand) {
|
|
|
240
258
|
case "help":
|
|
241
259
|
case "--help":
|
|
242
260
|
case "-h":
|
|
243
|
-
console.log(
|
|
261
|
+
console.log(buildHelp());
|
|
244
262
|
break;
|
|
245
263
|
|
|
246
264
|
default:
|
|
247
265
|
if (subcommand) console.error(`Unknown command: "${subcommand}"\n`);
|
|
248
|
-
console.log(
|
|
266
|
+
console.log(buildHelp());
|
|
249
267
|
process.exit(subcommand ? 1 : 0);
|
|
250
268
|
}
|
|
251
269
|
|