feat(picker): Visual HID/behavior picker built on upstream PR #159#4
Merged
Conversation
…picker Squashes upstream zmkfirmware#159 by @awkannan into a single commit. Replaces the HID usage dropdown with a categorized tab grid (Letters / Numbers + Punctuation / Function + Navigation / Numpad / Apps/Media/Special / International / Other) using react-aria-components Tabs, and keeps the implicit modifier checkboxes above the grid. - Renames src/hid-usage-name-overrides.json to src/hid-usage-metadata.json and extends entries with a "category" field plus richer labels (incl. Japanese/Korean IME-related keys). - Adds hid_usage_get_metadata() that falls back to the canonical name stripped of the "Keyboard " prefix when no override is present. - Updates HidUsageLabel to consume the new metadata accessor. Credit: zmkfirmware#159 (Andrew Kannan, @awkannan). Apache-2.0 license preserved. Co-Authored-By: Andrew Kannan <andrew.kannan@gmail.com> Co-Authored-By: Claude <noreply@anthropic.com>
Layered on top of upstream PR zmkfirmware#159's category-tabbed grid: - HidUsagePicker: a global filter input above the tabs. While the field has content, the tab list is replaced by a flat grid of all matching keys (matched against canonical HID name, short/med/long override labels, and category). The per-tab "Other" ComboBox is kept untouched so the upstream layout still works when the filter is empty. - ParameterValuePicker: the layer-id <select> is replaced with a wrap-friendly row of buttons styled like the keycode grid, so picking a target layer for `mo` / `lt` / `to` matches the visual feel of the rest of the picker. - keymap-parser: re-point the override JSON import at the renamed hid-usage-metadata.json (PR zmkfirmware#159 deleted the old name). Co-Authored-By: Claude <noreply@anthropic.com>
Replace the single behavior dropdown with a flat chip grid grouped by
tier ("ZMK Standard" / "Firmware Extension") and category
("Basic / Layer / Hold-Tap / Mouse / System / Custom / Other"). Chips
within a category are reordered so the most common behaviors land first
("Key Press" first, "Transparent / None" last). The block label uses a
shared uppercase-tracked style so it lines up with the keycode picker.
- BehaviorParametersPicker: show Hold/Tap tabs only for 2-param bindings
with a HID-typed param2, fall through to the picker directly for
1-param, and disable the field for 0-param behaviors. Defaults to the
first metadata set so param2 is visible even when param1 = 0.
- ParameterValuePicker: drop the native <select> for layer ids in favor
of a wrap-friendly button row styled to match the keycode grid.
Co-Authored-By: Claude <noreply@anthropic.com>
Layer ANSI 60% / TKL function+nav cluster / numpad layouts on top of upstream PR zmkfirmware#159's tab grid, so picking a keycode reads like a real keyboard. Modifiers and CapsLk fall back to short labels ("L Shft", "L Ctrl") and paired short labels ("1 !", "[ {") are stacked top-small / bottom-large to match the physical legend. - HidUsagePicker: split into "+ MODIFIER" (eight modifier column) and "KEY" (search + tabs + visual grid). Tabs: Basic (ANSI 60%), Function + Nav (TKL cluster), Numpad, Apps/Media/Special, International, Other. All tabs share a fixed min/max height so the panel doesn't jump when switching. Auto-jumps to the tab that holds the current value (or Basic when value is 0). - Search bar above the tabs filters across every category and renders a flat result grid while populated; clears restore the per-tab view. - hid-usage-metadata.json: drop JIS-only sub-labels from Intl1-6 and Lang1-5. Locale-specific glyphs were misleading on non-JP keyboards (e.g. Lang1/Lang2 type Hangul per spec but trigger Henkan/Muhenkan on JIS layouts), so labels stay neutral until a proper i18n overlay is introduced. Co-Authored-By: Claude <noreply@anthropic.com>
Make the keymap preview faithful to the bound behavior at a glance and
keep the rendering stable across zoom, rotation, and key width.
- Key + Keymap: render the behavior name in a rounded badge at the top
of each cap (clears with pt-3.5; no pad when no badge). Hold-tap
bindings stack the hold value small/faded above the bold tap value;
layer-type params render the layer name directly; constants and raw
values get type-aware text sizes. Drop the hover scale/3D translate
so popped keys don't overlap their neighbors.
- HidUsageLabel: render paired short labels ("1 !" → top-small "!" /
base "1") and collapse to the shifted glyph alone when an implicit
Shift modifier is set ("LS(N2)" → "@"). Auto-shrink labels longer
than 4 glyphs so values like "Lang1" fit in 1U keys without ellipsis.
Add a `compact` mode used by hold-tap previews where vertical room is
scarce.
- PhysicalLayout: bbox the layout with rotation taken into account so
rotated thumb keys no longer clip, and switch the scale wrapper to
observe only the parent (element observation caused a feedback loop
on zoom-to-fit).
- Keyboard: pin the bottom panel's grid row to a fixed width with
overflow auto so picker tab changes don't jump the layout. Surface
setLayerBinding rejections via a toast that names the behavior and
hints that it may not be writable from Studio in this firmware build.
Co-Authored-By: Claude <noreply@anthropic.com>
Two follow-ups from a self-review of the visual-picker branch. - Keyboard: the undo path of setLayerBinding had an empty else, so a firmware-rejected Ctrl+Z would silently leave the UI out of sync with the device. Mirror the do-path notify() with an undo-specific message. - BehaviorParametersPicker: when the user switches from a 2-param behavior to a 1-param one while the Tap tab is active, activeParam stays at "p2" and feeds <Tabs> a key for a tab that no longer exists. Snap it back to "p1" via useEffect when hasParam2 flips false. Co-Authored-By: Claude <noreply@anthropic.com>
numachang
added a commit
that referenced
this pull request
Jun 7, 2026
- formatBinding: when param1 matches no metadata set, emit just the behavior reference rather than borrowing the first set's params (avoids showing a param2 that doesn't apply). [review #1] - Behavior tiles are single-select, so make them `role="radio"` inside a `radiogroup` (consistent with the residual behavior chips) instead of `aria-pressed` toggles; native button + `title` keeps the visible label and drops the now-redundant tooltip. Search-result tiles get their own radiogroup. [review #2] - Annotate the two intentional exhaustive-deps effects with `eslint-disable-next-line` so the warnings clear and the intent is explicit. [review #4] Review #3 (declaration-order of the two tab-select effects) was info-only / no change requested; left as-is. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Personal-fork visual rework of the binding picker, layered on top of upstream zmkfirmware/zmk-studio#159 (Andrew Kannan,
@awkannan).453980d) with attribution preserved.<select>with a wrap-friendly button row (1d7de0e).931edf0).Intl1-6/Lang1-5(the per-region glyphs were misleading on non-JP keyboards — left for a future locale overlay) (0e226bf).1 !→ stacked), shrink-to-fit for 5+ glyph labels, rotation-aware bounding box (rotated thumb keys no longer clip), and anotify()toast when the firmware rejectssetLayerBinding(c3b5cad).activeParamresets top1when the active tab disappears after a behavior swap (50c47a6).Upstream stance
This is a personal fork, intentionally minimal-diff and Apache-2.0. Apache notices and
@awkannan's authorship for PR zmkfirmware#159 are preserved. Individual pieces will be proposed upstream as separate PRs (rotation bbox fix, undo-rejection toast, etc. as small standalone bug fixes; the chip grid and physical layouts as opinionated UX proposals).Test plan
Open
npm run devin real Chrome/Edge (Web Serial / BLE will not work in VSCode's embedded browser):&kp→ HID grid auto-jumps to the Basic tab and renders the ANSI 60% layoutShft) → paired keys collapse to the shifted glyph (1 !→!)&mt LSHFT A→ keymap preview shows hold faded above bold tap, Hold/Tap tabs appear in the param pickercapsin the search bar → flat results grid filters across all tabs; clearing restores per-tab viewmouse_moveon a build without it) → error toast appears with hint text