diff --git a/src/device/controller.ts b/src/device/controller.ts index 7ece321e..8fe6b50f 100644 --- a/src/device/controller.ts +++ b/src/device/controller.ts @@ -265,6 +265,7 @@ const needsOpenClasses = (): Array | undefined> => CM()?.MchoseHidClient, CM()?.MchoseDockHidClient, CM()?.MchoseA5ProMaxHidClient, CM()?.MchoseV3HidClient, CM()?.MicrosoftHidClient, CM()?.DareuHidClient, CM()?.IncottHidClient, CM()?.BytechHidClient, CM()?.RapooHidClient, CM()?.FaterHidClient, CM()?.CoolerMasterHidClient, CM()?.RedragonM690ProHidClient, CM()?.AjazzHidClient, + CM()?.HitscanHidClient, ]; const pulsarClasses = (): Array | undefined> => [CM()?.PulsarHidClient, CM()?.PulsarProHidClient, CM()?.PulsarXs1HidClient]; diff --git a/src/device/driver-classes.ts b/src/device/driver-classes.ts index 49114273..2187c1f3 100644 --- a/src/device/driver-classes.ts +++ b/src/device/driver-classes.ts @@ -76,7 +76,8 @@ export type DriverModules = typeof import("@openmouse/protocol/drivers/endgame/e & typeof import("@openmouse/protocol/drivers/rapoo/hid") & typeof import("@openmouse/protocol/drivers/coolermaster/hid") & typeof import("@openmouse/protocol/drivers/ajazz/hid") - & typeof import("@openmouse/protocol/drivers/redragon/m690-pro-hid"); + & typeof import("@openmouse/protocol/drivers/redragon/m690-pro-hid") + & typeof import("@openmouse/protocol/drivers/hitscan/hid"); let loaded: DriverModules | null = null; let loading: Promise | null = null; @@ -143,6 +144,7 @@ export function ensureDriverModules(): Promise { import("@openmouse/protocol/drivers/coolermaster/hid"), import("@openmouse/protocol/drivers/ajazz/hid"), import("@openmouse/protocol/drivers/redragon/m690-pro-hid"), + import("@openmouse/protocol/drivers/hitscan/hid"), ]).then((mods) => (loaded = Object.assign({}, ...mods) as DriverModules)); return loading; } diff --git a/src/device/traits.ts b/src/device/traits.ts index 15052eea..00159e46 100644 --- a/src/device/traits.ts +++ b/src/device/traits.ts @@ -101,6 +101,9 @@ const BY_FAMILY: Readonly>> = { // The NJ07/NJ08 family keeps the sleep timer in its config block and has no // debounce setting in the vendor panel, so only the sleep card. ajazz: { advancedSection: true, sleep: true }, + // Hitscan stores a 0-8 ms debounce in flash and publishes its own ceiling. + // Its sleep units are unresolved, so the driver hides the sleep card. + hitscan: { advancedSection: true, debounce: true }, }; const BY_BRAND: Readonly> = { diff --git a/src/features/devices/cards/availability.test.ts b/src/features/devices/cards/availability.test.ts index e3b08b60..6f051a62 100644 --- a/src/features/devices/cards/availability.test.ts +++ b/src/features/devices/cards/availability.test.ts @@ -402,6 +402,16 @@ test("debounce is offered only by the families that store it", () => { })).debounce, false); }); +test("the Hitscan family alone gives the advanced section and debounce, not sleep or signal", () => { + // No ui flags beyond the family, so the result comes from the traits entry. + const status = { brand: "Hitscan", ui: { family: "hitscan" }, debounceMs: 4, sleepTimeout: 60, signalStrength: 80 } as const; + const has = cardAvailability(snapshot({ status })); + assert.equal(traitsFor({ ...STATUS, ...status }).advancedSection, true); + assert.equal(has.debounce, true); + assert.equal(has.sleep, false); + assert.equal(has.signal, false); +}); + test("low power needs the Razer capability, not merely a reported threshold", () => { // selectableValues treats an empty option list as unbounded, so a mouse that // reports a threshold without the Razer driver must not show the card.