{"id":24189369,"url":"https://github.com/devsunb/kanata-vk-agent","last_synced_at":"2025-03-03T03:21:54.186Z","repository":{"id":269677302,"uuid":"907941434","full_name":"devsunb/kanata-vk-agent","owner":"devsunb","description":"Control kanata virtual keys while observing frontmost app and input source on macOS to enable application and input source aware key mapping","archived":false,"fork":false,"pushed_at":"2025-02-15T10:09:28.000Z","size":51,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-15T11:20:32.750Z","etag":null,"topics":["kanata","keyboard","macos","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devsunb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-24T16:53:58.000Z","updated_at":"2025-02-15T10:09:28.000Z","dependencies_parsed_at":"2025-01-13T14:33:59.660Z","dependency_job_id":"e207383b-f675-4fa4-a6ed-cd74771d995c","html_url":"https://github.com/devsunb/kanata-vk-agent","commit_stats":null,"previous_names":["devsunb/kanata-appvk"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devsunb%2Fkanata-vk-agent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devsunb%2Fkanata-vk-agent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devsunb%2Fkanata-vk-agent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devsunb%2Fkanata-vk-agent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devsunb","download_url":"https://codeload.github.com/devsunb/kanata-vk-agent/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241601163,"owners_count":19988878,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["kanata","keyboard","macos","rust"],"created_at":"2025-01-13T14:29:28.554Z","updated_at":"2025-03-03T03:21:54.172Z","avatar_url":"https://github.com/devsunb.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# kanata-vk-agent\n\nControl kanata virtual keys while observing frontmost app and input source on macOS to enable application and input source aware key mapping\n\n## What does this do?\n\n- on start\n  - release all virtual keys passed as options\n  - press the virtual keys named with the id of the frontmost app and current input source\n- when the frontmost app or input source changes\n  - release the pressed virtual key\n  - press the virtual key named with the id of the new frontmost app or input source\n\n## Install\n\n### Homebrew\n\n```sh\nbrew tap devsunb/tap\nbrew install kanata-vk-agent\n```\n\n### Build from source\n\n```sh\ngit clone https://github.com/devsunb/kanata-vk-agent.git\ncd kanata-vk-agent\ncargo build --release\ncp target/release/kanata-vk-agent \"$HOME/.local/bin/kanata-vk-agent\"\n```\n\n## Running via launchd\n\nIf you're interested in using launchd to automatically run in the background, see the following script.\n\n```sh\nLAUNCH_AGENTS_PATH=\"$HOME/Library/LaunchAgents\"\nKANATA_VK_AGENT_ID=\"local.kanata-vk-agent\"\nKANATA_VK_AGENT=\"$HOME/.local/bin/kanata-vk-agent\"\nKANATA_VK_AGENT_PLIST=\"$LAUNCH_AGENTS_PATH/$KANATA_VK_AGENT_ID.plist\"\n\ncat \u003c\u003cEOF | tee \"$KANATA_VK_AGENT_PLIST\" \u003e/dev/null\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003c!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"\u003e\n\u003cplist version=\"1.0\"\u003e\n  \u003cdict\u003e\n    \u003ckey\u003eLabel\u003c/key\u003e\n    \u003cstring\u003e$KANATA_VK_AGENT_ID\u003c/string\u003e\n\n    \u003ckey\u003eProgramArguments\u003c/key\u003e\n    \u003carray\u003e\n      \u003cstring\u003e$KANATA_VK_AGENT\u003c/string\u003e\n      \u003cstring\u003e-p\u003c/string\u003e\n      \u003cstring\u003e5829\u003c/string\u003e\n      \u003cstring\u003e-b\u003c/string\u003e\n      \u003cstring\u003ecom.apple.Safari,org.mozilla.firefox\u003c/string\u003e\n      \u003cstring\u003e-i\u003c/string\u003e\n      \u003cstring\u003ecom.apple.keylayout.ABC,com.apple.inputmethod.Korean.2SetKorean\u003c/string\u003e\n    \u003c/array\u003e\n\n    \u003ckey\u003eRunAtLoad\u003c/key\u003e\n    \u003ctrue /\u003e\n\n    \u003ckey\u003eKeepAlive\u003c/key\u003e\n    \u003cdict\u003e\n      \u003ckey\u003eCrashed\u003c/key\u003e\n      \u003ctrue /\u003e\n      \u003ckey\u003eSuccessfulExit\u003c/key\u003e\n      \u003cfalse /\u003e\n    \u003c/dict\u003e\n\n    \u003ckey\u003eStandardOutPath\u003c/key\u003e\n    \u003cstring\u003e/tmp/kanata_vk_agent_stdout.log\u003c/string\u003e\n    \u003ckey\u003eStandardErrorPath\u003c/key\u003e\n    \u003cstring\u003e/tmp/kanata_vk_agent_stderr.log\u003c/string\u003e\n  \u003c/dict\u003e\n\u003c/plist\u003e\nEOF\n\nlaunchctl bootout gui/501 \"$KANATA_VK_AGENT_PLIST\" 2\u003e/dev/null || true\nlaunchctl bootstrap gui/501 \"$KANATA_VK_AGENT_PLIST\"\nlaunchctl enable \"gui/501/$KANATA_VK_AGENT_ID\"\n```\n\nYou may need to modify the executable(`$HOME/.local/bin/`) and log(`/tmp/`) path, uid(`501`), port(`5829`), bundle ids(`com.apple.Safari,org.mozilla.firefox`), input source ids(`com.apple.keylayout.ABC,com.apple.inputmethod.Korean.2SetKorean`), etc. to suit your system.\n\n## Usage\n\n```sh\n$ kanata-vk-agent --help\n\nControl kanata virtual keys while observing frontmost app and input source on macOS\n\nExample: kanata-vk-agent -p 5829 -b com.apple.Safari,org.mozilla.firefox -i com.apple.keylayout.ABC,com.apple.inputmethod.Korean.2SetKorean\n\nUsage: kanata-vk-agent [OPTIONS]\n\nOptions:\n  -l, --log-level \u003cLOG_LEVEL\u003e\n          Log level\n\n          [default: info]\n          [possible values: off, error, warn, info, debug, trace]\n\n  -p, --port \u003cPORT\u003e\n          TCP port number of kanata\n\n          [default: 5829]\n\n  -b, --bundle-ids \u003cBUNDLE_IDS\u003e\n          Bundle Identifiers, each of which is the name of a virtual key\n\n  -i, --input-source-ids \u003cINPUT_SOURCE_IDS\u003e\n          Input Source Identifiers, each of which is the name of a virtual key\n\n  -f, --find-id-mode\n          Just print the app's bundle id or input source id when the frontmost app and input source change. In this mode, it will not connect to kanata\n\n  -h, --help\n          Print help (see a summary with '-h')\n\n  -V, --version\n          Print version\n```\n\n## Prerequisites\n\n- kanata must be running on localhost with the TCP port set\n- Make sure all bundle ids and input source ids are defined in defvirtualkeys in your kanata configuration.\n  - The action for each virtual key can be any value (when in doubt, use nop0)\n  - Use [switch and input syntax](https://jtroo.github.io/config.html#switch) for application and input source aware key action\n\n## kanata configuration example\n\n```kbd\n(defsrc\n  1\n)\n\n(deflayer test\n  @test\n)\n\n(defvirtualkeys\n  com.apple.Safari nop0\n  org.mozilla.firefox nop0\n  com.github.wez.wezterm nop0\n\n  com.apple.keylayout.ABC nop0\n  com.apple.inputmethod.Korean.2SetKorean nop0\n)\n\n(defalias\n  test (switch\n    ((input virtual com.apple.Safari)) 2 break\n    ((input virtual org.mozilla.firefox)) 3 break\n    () 1 break\n  )\n)\n```\n\nThe above kanata configuration would make the key `1` act as `2` in Safari, `3` in Firefox, and `1` in other apps.\n\nYou can use a similar structure to define keys that take different actions based on the input source.\nThis can be useful for users who want to use the Colemak layout for English and the Qwerty layout for other input sources, such as Korean.\n\nkanata-vk-agent should be running with a command like:\n\n```sh\nkanata-vk-agent -p 5829 -b com.apple.Safari,org.mozilla.firefox,com.github.wez.wezterm -i com.apple.keylayout.ABC,com.apple.inputmethod.Korean.2SetKorean\n```\n\nUnless the current frontmost app is one of the apps passed with the `-b` option, all bundle id virtual keys will be in the released state.\nThis means that in the above configuration, if you switch apps in the order Safari - Finder - Firefox,\nthe activated bundle id virtual key will be `com.apple.Safari` - (all released) - `org.mozilla.firefox`, respectively.\n\nIf you're curious about what your app's bundle id or input source's id is, use the `-f` option.\nkanata-vk-agent will not connect to kanata, and will just print the id of the frontmost app and current input source when they change.\n\n```sh\nkanata-vk-agent -f\n```\n\n## Background\n\nI'm a macOS user, and until I started using [kanata](https://github.com/jtroo/kanata), I was using [Karabiner-Elements](https://github.com/pqrs-org/Karabiner-Elements) for conditional (like application and input source) key mapping.\n\nkanata uses [Karabiner-DriverKit-VirtualHIDDevice](https://github.com/pqrs-org/Karabiner-DriverKit-VirtualHIDDevice) internally on macOS, so it cannot be used with Karabiner-Elements. [kanata#1211](https://github.com/jtroo/kanata/issues/1211)\n\nThe reason I decided to use kanata instead of Karabiner-Elements is that kanata has many features such as layers and macros that are difficult to implement in Karabiner-Elements.\n\nHowever, one thing I missed when switching from Karabiner-Elements to kanata was conditional key mapping.\nkanata made it clear that application-aware layer switching should be done through an external tool, not within kanata. [kanata#770](https://github.com/jtroo/kanata/discussions/770)\n\nIt seemed that many people had already developed tools for Linux and Windows,\n([Community projects related to kanata](https://github.com/jtroo/kanata#community-projects-related-to-kanata))\nbut I couldn't find one for macOS. So I decided to create a kanata helper tool for macOS.\n\nInitially, I tried to switch layers, but realized that for simple configuration and to avoid tricky situations like switching apps during layer-toggle(layer-while-held),\nit was better to use virtual keys rather than layers. So I created a tool to enable conditional key mapping based on virtual keys.\n\n## Note\n\n- This tool uses Apple's macOS Objective-C frameworks, so it only supports macOS\n- kanata allows [up to 767 virtual keys](https://jtroo.github.io/config.html#virtual-keys)\n\n### live reload\n\nkanata [live reload](https://jtroo.github.io/config.html#live-reload) does not run when the virtual key is pressed,\nso it will run on the next time all virtual keys are released.\n\n```\n...\n18:06:24.3392 [INFO] Requested live reload of file: /Users/sunb/dev/dotfiles/kanata/kanata.kbd\n// Live reload triggered but not executed because the virtual key is pressed\n18:06:25.1873 [INFO] tcp server fake-key action: org.mozilla.firefox,Release\n18:06:25.1875 [INFO] tcp server fake-key action: com.apple.Safari,Press\n// Frontmost app switched Firefox to Safari but live reload still not executed because both app included in bundle ids\n18:06:28.0506 [INFO] tcp server fake-key action: com.apple.Safari,Release\n18:06:28.0525 [INFO] process unmapped keys: true\n// Frontmost app switched to Finder and live reload executed\n...\n```\n\nIf you pass all the input sources you're using as input-source-ids,\nyou might never get to a state where all virtual keys are released,\nin which case live reload is not available at this time.\n\n## Credit\n\n- [appwatcher](https://github.com/meschbach/appwatcher): Example of using cgo to execute go code when macOS frontmost app changes\n- [clavy](https://github.com/rami3l/clavy): I had never used rust before, so I tried to build it in go, but then I saw that this repository uses objc2 libraries, so I was able to rebuild it in rust.\n- [qanata](https://github.com/veyxov/qanata): Referenced how to communicate over a TCP connection with kanata in rust.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevsunb%2Fkanata-vk-agent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevsunb%2Fkanata-vk-agent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevsunb%2Fkanata-vk-agent/lists"}