diff --git a/src/app/HardwareTestPage.tsx b/src/app/HardwareTestPage.tsx index 5f50c7ed..88810cbb 100644 --- a/src/app/HardwareTestPage.tsx +++ b/src/app/HardwareTestPage.tsx @@ -337,7 +337,7 @@ export function HardwareTestPage({ snapshot }: { snapshot: ControlSnapshot }): R if (lodCurrent === null) { legs.push({ setting: "Lift-off", current: "—", target: "—", status: "skip", detail: "no lift-off distance is reported on this device." }); } else { - const lodTarget = pickFlashLiftOffTarget(lodCurrent); + const lodTarget = pickFlashLiftOffTarget(lodCurrent, live.status.supportedLiftOffDistances); if (lodTarget === null) { legs.push({ setting: "Lift-off", current: lodCurrent, target: "—", status: "skip", detail: "no alternate lift-off distance is available." }); } else { diff --git a/src/app/cards/availability.test.ts b/src/app/cards/availability.test.ts index fefeb64e..b732b5b0 100644 --- a/src/app/cards/availability.test.ts +++ b/src/app/cards/availability.test.ts @@ -347,6 +347,24 @@ test("the generic button card follows the driver's mappings, not the advanced se assert.equal(has.processing, false, "the rest of the advanced section stays closed"); }); +test("the G-Wolves XVI generation opens sleep, debounce and processing, but not signal", () => { + const cards = cardAvailability(snapshot({ + status: { + brand: "G-Wolves", + ui: { family: "gwolves-xvi" }, + motionSync: false, + angleSnapping: true, + rippleControl: null, + debounceMs: 2, + sleepTimeout: 60, + }, + })); + assert.equal(cards.sleep, true); + assert.equal(cards.debounce, true); + assert.equal(cards.processing, true); + assert.equal(cards.signal, false); +}); + test("the Incott card follows the fields the device reported, not a brand trait", () => { // Wireless: both controls are present. const wireless = cardAvailability(snapshot({ diff --git a/src/device/traits.ts b/src/device/traits.ts index 70f9abab..622b54f3 100644 --- a/src/device/traits.ts +++ b/src/device/traits.ts @@ -78,6 +78,9 @@ const BY_FAMILY: Readonly>> = { // from ui.showAdvancedSection, but sleep/debounce still need the flags // here or the cards never render regardless of applyPulsarValue's list). incott: { advancedSection: true, sleep: true, debounce: true }, + // The G-Wolves XVI generation (HTX Mini 8K) publishes its own sleep list + // and reads its before-press debounce; no signal-strength command exists. + "gwolves-xvi": { advancedSection: true, sleep: true, debounce: true }, // HyperX publishes DPI, polling rate and lift-off in the settings grid only; // no signal, sleep or debounce card exists, and the processing card is // deliberately hidden, so no advanced-section flags are needed. diff --git a/src/hardware-test-report.test.ts b/src/hardware-test-report.test.ts index 088c0e3f..138adb92 100644 --- a/src/hardware-test-report.test.ts +++ b/src/hardware-test-report.test.ts @@ -273,6 +273,9 @@ test("pickFlashLiftOffTarget prefers Medium, falling back to Low", () => { assert.equal(pickFlashLiftOffTarget("High"), "Medium"); assert.equal(pickFlashLiftOffTarget("Medium"), "Low"); assert.equal(pickFlashLiftOffTarget(null), null); + assert.equal(pickFlashLiftOffTarget("Low", ["Low", "High"]), "High"); + assert.equal(pickFlashLiftOffTarget("High", ["Low", "High"]), "Low"); + assert.equal(pickFlashLiftOffTarget("Low", ["Low"]), null); }); test("flashWriteResult scores the round-trip legs — DPI, poll rate, lift-off", () => { diff --git a/src/hardware-test-report.ts b/src/hardware-test-report.ts index 9924965f..47629f5f 100644 --- a/src/hardware-test-report.ts +++ b/src/hardware-test-report.ts @@ -392,15 +392,17 @@ export function pickFlashPollRateTarget(current: number | null, options: readonl } /** - * Picks the least obtrusive alternate lift-off level. Medium is the middle of - * the physical range, so it is preferred whenever the device is not already on - * it; a Medium device falls back to Low. + * Picks the least obtrusive alternate lift-off level the device offers. + * Medium is the middle of the physical range, so it is preferred whenever the + * device is not already on it, then Low, then High (a Low/High-only mouse + * such as the G-Wolves HTX Mini). */ export function pickFlashLiftOffTarget( current: "Low" | "Medium" | "High" | null, + supported: readonly ("Low" | "Medium" | "High")[] = ["Low", "Medium", "High"], ): "Low" | "Medium" | "High" | null { if (current === null) return null; - return current === "Medium" ? "Low" : "Medium"; + return (["Medium", "Low", "High"] as const).find((level) => level !== current && supported.includes(level)) ?? null; } /** One setting's write → read-back → restore leg of the flash round-trip. */