From cdd2f77e63908c6295f0ea65017937a6055ba80d Mon Sep 17 00:00:00 2001 From: ydw1904 Date: Wed, 30 Sep 2026 01:10:12 +0800 Subject: [PATCH] feat(keychron): wire the G3 Air into the panel Keychron8kNordicHidClient (family keychron-8k-nordic, from mouse-protocol) gets the advanced section with the sleep and debounce cards, since it reads and writes both and publishes its own option lists. It joins NEEDS_OPEN so the device opens before the first read. DPI stages, polling, lift-off, the processing toggles, angle tuning, profiles and the button remapper all dispatch generically. Needs a protocol release with the driver and a bump from 0.19.0. Co-Authored-By: Claude Opus 5.5 --- src/device/controller.ts | 3 ++- src/device/traits.ts | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/device/controller.ts b/src/device/controller.ts index 3c5ec76..4b199e6 100644 --- a/src/device/controller.ts +++ b/src/device/controller.ts @@ -138,6 +138,7 @@ import { VgnF2HidClient } from "@openmouse/protocol/drivers/vgn/hid"; import { KeychronNapeHidClient } from "@openmouse/protocol/drivers/keychron/nape-hid"; import { KeychronM6HidClient } from "@openmouse/protocol/drivers/keychron/m6-hid"; import { Keychron4kHidClient } from "@openmouse/protocol/drivers/keychron/mouse-4k-hid"; +import { Keychron8kNordicHidClient } from "@openmouse/protocol/drivers/keychron/mouse-8k-nordic-hid"; import type { GloriousLighting } from "@openmouse/protocol/glorious"; import { GloriousHidClient } from "@openmouse/protocol/drivers/glorious/hid"; import { GloriousClassicHidClient } from "@openmouse/protocol/drivers/glorious/classic-hid"; @@ -243,7 +244,7 @@ function activeAs(...classes: ClientClass[]): T | null { const DM_CLASSES = [WLMouseHidClient, LamzuHidClient, LamzuAtlantisHidClient, AtkHidClient, AtkBitmouseHidClient, NinjutsoHidClient] as const; const RAZER_CLASSES = [RazerHidClient, RazerViperMiniHidClient, RazerViperHidClient, RazerCobraHidClient] as const; -const NEEDS_OPEN = [LamzuAtlantisHidClient, TeevolutionHidClient, VgnF2HidClient, KeychronNapeHidClient, KeychronM6HidClient, WLMouseBeastX4kHidClient, ModdoHidClient, ZaunkoenigHidClient, CorsairHidClient, FantechHidClient, WallhackMouseHidClient, WallhackKeyboardHidClient, GloriousHidClient, GloriousClassicHidClient, MchoseHidClient, MchoseDockHidClient, MchoseA5ProMaxHidClient, MchoseV3HidClient, MicrosoftHidClient, DareuHidClient, IncottHidClient, BytechHidClient] as const; +const NEEDS_OPEN = [LamzuAtlantisHidClient, TeevolutionHidClient, VgnF2HidClient, KeychronNapeHidClient, KeychronM6HidClient, Keychron8kNordicHidClient, WLMouseBeastX4kHidClient, ModdoHidClient, ZaunkoenigHidClient, CorsairHidClient, FantechHidClient, WallhackMouseHidClient, WallhackKeyboardHidClient, GloriousHidClient, GloriousClassicHidClient, MchoseHidClient, MchoseDockHidClient, MchoseA5ProMaxHidClient, MchoseV3HidClient, MicrosoftHidClient, DareuHidClient, IncottHidClient, BytechHidClient] as const; const PULSAR_CLASSES = [PulsarHidClient, PulsarProHidClient, PulsarXs1HidClient] as const; const logitechClient = (): LogitechHidppClient | null => activeAs(LogitechHidppClient); diff --git a/src/device/traits.ts b/src/device/traits.ts index 622b54f..737f986 100644 --- a/src/device/traits.ts +++ b/src/device/traits.ts @@ -53,6 +53,9 @@ const BY_FAMILY: Readonly>> = { "keychron-m6": { advancedSection: true, sleep: true, debounce: true }, // Launcher offers no sleep setting for the 4K family, so only debounce. "keychron-4k": { advancedSection: true, debounce: true }, + // The 8K Nordic family (G3 Air) keeps debounce and sleep in its system block + // and publishes its own option lists for both. + "keychron-8k-nordic": { advancedSection: true, sleep: true, debounce: true }, // The Beast X 4K skips the compx transport the rest of WLMouse uses and // publishes its own sleep and debounce lists, so it takes the plain flags. "wlmouse-4k": { advancedSection: true, sleep: true, debounce: true },