* xdotool-style sequence e.g. "ctrl+shift+a" → split on '+' and pass to * keys(). keys() dispatches to DispatchQueue.main — drainRunLoop pumps * CFRunLoop so it resolves. Rust's error-path cleanup (enigo_wrap.rs) * releases modifiers on each invocation, so a mid-loop throw leaves
(keySequence: string, repeat?: number)
| 527 | * nothing stuck. 8ms between iterations — 125Hz USB polling cadence. |
| 528 | */ |
| 529 | async key(keySequence: string, repeat?: number): Promise<void> { |
| 530 | const input = requireComputerUseInput() |
| 531 | const parts = keySequence.split('+').filter(p => p.length > 0) |
| 532 | const isEsc = isBareEscape(parts) |
| 533 | const n = repeat ?? 1 |
| 534 | await drainRunLoop(async () => { |
| 535 | for (let i = 0; i < n; i++) { |
| 536 | if (i > 0) await sleep(8) |
| 537 | if (isEsc) notifyExpectedEscape() |
| 538 | await input.keys(parts) |
| 539 | } |
| 540 | }) |
| 541 | }, |
| 542 | |
| 543 | async holdKey(keyNames: string[], durationMs: number): Promise<void> { |
| 544 | const input = requireComputerUseInput() |
nothing calls this directly
no test coverage detected