{"id":18999085,"url":"https://github.com/crossr/kb_ui","last_synced_at":"2026-02-15T04:35:43.962Z","repository":{"id":139441058,"uuid":"476692228","full_name":"CrossR/kb_ui","owner":"CrossR","description":"A tiny systray utility to keep track of the current keyboard layer.","archived":false,"fork":false,"pushed_at":"2024-05-13T15:37:11.000Z","size":151,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-13T21:41:14.315Z","etag":null,"topics":["golang","keyboard","systray","zmk"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CrossR.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2022-04-01T11:27:22.000Z","updated_at":"2024-05-13T15:37:13.000Z","dependencies_parsed_at":"2024-05-13T17:01:03.007Z","dependency_job_id":null,"html_url":"https://github.com/CrossR/kb_ui","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/CrossR/kb_ui","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CrossR%2Fkb_ui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CrossR%2Fkb_ui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CrossR%2Fkb_ui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CrossR%2Fkb_ui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CrossR","download_url":"https://codeload.github.com/CrossR/kb_ui/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CrossR%2Fkb_ui/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29469585,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-15T04:35:06.950Z","status":"ssl_error","status_checked_at":"2026-02-15T04:33:41.357Z","response_time":118,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["golang","keyboard","systray","zmk"],"created_at":"2024-11-08T17:49:15.977Z","updated_at":"2026-02-15T04:35:43.930Z","avatar_url":"https://github.com/CrossR.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# kb_ui - Keyboard System Tray Tool\n\nA tiny systray utility to help me keep track of keyboard layers, showing the current layer.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://user-images.githubusercontent.com/10038688/162264281-c1dbca2d-025b-4091-bdd8-d931679aafe8.PNG\"\u003e\n\u003c/p\u003e\n\n## What?\n\nI use a [ZMK](https://zmk.dev) firmware powered board which can have multiple layers of\nkeybindings, but I don't have a screen or LEDs on the board to keep track of the current\nlayer.\n\nSince I use certain bindings that conflict depending on the use case (i.e. `a` acts as\nboth the letter `a` and as a layer swap key, depending on if its tapped or held), I wanted\na very simple way to check the current layer. That way, when I need `a` to be held (say if\nI'm playing a game), I can make sure the keyboard is in game mode and not typing mode.\nSimilarly, I have a different layout when I'm on Mac vs Windows.\n\n## Examples\n\nStarting from some basic, default state...\n\n\u003cimg src=\"https://user-images.githubusercontent.com/10038688/162264281-c1dbca2d-025b-4091-bdd8-d931679aafe8.PNG\"\u003e\n\nLets swap to a gaming mode by pressing the layer change button on my board...\n\n\u003cimg src=\"https://user-images.githubusercontent.com/10038688/162264282-ffb7d41c-5a48-4ae0-b8d9-5a15b53cef58.PNG\"\u003e\n\nOr perhaps to one setup for mac...\n\n\u003cimg src=\"https://user-images.githubusercontent.com/10038688/162264284-48982bcd-5d9d-4797-8be6-2792a991e452.PNG\"\u003e\n\nOr toggle the output from USB to bluetooth...\n\n\u003cimg src=\"https://user-images.githubusercontent.com/10038688/162264280-ab094c1a-7524-4c77-91b4-4bd54303fb07.PNG\"\u003e\n\n\n## Technology\n\nWritten in golang, to produce a single, self-contained binary for every platform. That\nis, when I press a layer swap key (to move into gaming mode for example), as well as\nswapping layer, a unique keycode is sent to the system that `kb_ui` reacts to, swapping\nthe current state of the tray, updating the tray icon to a user selected icon.\nLayers are defined in a json file with their name, icon and the input key-press\nthat activates it.\n\n## Setup\n\nAs stated previously, this doesn't use any HID features or anything so smart, it\nrelies on keycodes being sent to the host. This means my layer change bindings\nhad to be updated to include these unique keycodes, and also tell `kb_ui` what they\nare.\n\nOn the `kb_ui` side, launch it by double clicking, then right click the system tray\nicon, configure. That should open up the default JSON config in your editor (or copy\nthe path and open it yourself if it defaults to your browser).\n\nMost importantly, there are the icon swap bindings, defined as follows:\n\n```json\n{\n    \"key\": \"1\",\n    \"mods\": \"ctrl-shift-win-alt\",\n    \"name\": \"Default\",\n    \"icon\": \"kb_light\",\n    \"dark_icon\": \"kb_dark\"\n}\n```\n\nWhere `key` defines the input key that will be pressed, `mods` the sequence of\nmodifier keys that will be held at the same time. `name` is the name you want to\ngive the layer, `icon` and `dark_icon` are relative paths to the icon that you\nwant to use, in `ico` format (`kb_light`, `kb_dark` and `disconnected` are built\nin icons, so just use strings).\n\nThe connect toggle binding is set individually, but works the same.\nThe connect toggle binding swaps the icon between the current layer icon, and\nthe disconnect icon (to show output device state).\n\nAn example of my config can be found\n[here](https://github.com/CrossR/dotfiles/tree/master/kb_ui/.config/kb_ui).\n\nSimilarly, my ZMK config, with the macros to actually output these layer change\nkeybinds can be found\n[here](https://github.com/CrossR/zmk_config/blob/master/config/sofle.keymap).\nThese are just simple macros that send the corresponding keybinding that I've\ndefined in my config when I swap layer / swap output device.\n\n## Limitations\n\nThere is the obvious limitation here, that if I swap my board to my Mac, and\nthen swap to a mac based layout... my PC has no idea about the layer change, so\nwhen I swap back its out of sync. A pain sure, but one swap and it gets back in\nsync and it hardly seems worth fixing when ZMK should in the future be able to\ntell the current layer easily enough. So I'll swap to HID codes then, rather\nthan fixing the odd edge case now.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrossr%2Fkb_ui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcrossr%2Fkb_ui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrossr%2Fkb_ui/lists"}