Skip to content

feat: list keyboard input sources and select one by ID - #90

Merged
iamnbutler merged 1 commit into
mainfrom
feat/input-source
Oct 17, 2026
Merged

iamnbutler merged 1 commit into
mainfrom
feat/input-source

Conversation

@iamnbutler

Copy link
Copy Markdown
Contributor

New tool desktop_input_source, item 2 of the #75 top 3 (awkward 10).

  • Without select, it lists the enabled, select-capable keyboard input sources from TISCreateInputSourceList.
    • Each source has id, name, kind (keyboard_layout, input_mode or input_method), its first 8 languages, and selected.
    • The result also gives current and keyboard_layout, the layout desktop_key maps through.
    • This reads only.
  • With select: <id>, it selects the source with TISSelectInputSource and polls TISCopyCurrentKeyboardInputSource for up to 1 s.
    • completed: the source reads back as selected, with before, current and changed.
    • Already selected: completed with changed: false, and nothing is called.
    • Refused before the call: an ID that isn't enabled, a locked session, or a process outside a GUI session.
    • Refused after the call: a non-noErr status with the source unchanged.
    • unknown: no readback within the second.
    • For an input method that reads back as selected, a note says that the app may pick up the mode only after its next focus change.
  • Where it runs: the client, not the host. The client already reads TIS for desktop_apps system.input. It's spawned by the MCP server in the user's GUI session, and TIS state is per session, so no Bridge op or patch is needed. All TIS calls run on the main actor (TSM asserts the main queue on recent macOS), where the async main's run loop delivers TIS changes for the readback. If the client isn't in a GUI session (CGSessionCopyCurrentDictionary is nil), selection is refused.
  • Wiring:
    • op input-source, shaped like dock: listing is a read, selecting is an action.
    • TS validates the ID: 1–256 characters, no control characters or whitespace.
    • docs/tools.md has a table row and a paragraph.
    • The "Switch the input source" recipe is rewritten around the tool.

Tests:

  • test/inputsource.test.mjs covers validation and the tool schema.
  • npm test passes 36 of 36, and npm run check passes.
  • The native build and the signed dev app build both pass.

Live, with the screen locked:

  • The list returned current: com.apple.keylayout.Canadian, with one source and its languages capped at 8.
  • Selecting com.apple.keylayout.US was refused: "The macOS GUI session is locked. The input source was not changed."
  • Pending (screen locked): an actual switch and readback, back and forth with a second enabled layout, plus an input method mode. Added to Run the live checks deferred while the screen was locked #42.

@iamnbutler
iamnbutler merged commit 20ce310 into main Oct 17, 2026
2 checks passed
@iamnbutler
iamnbutler deleted the feat/input-source branch October 17, 2026 07:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant