diff --git a/src/drivers/logitech/onboard-profiles.test.ts b/src/drivers/logitech/onboard-profiles.test.ts index 2c2f19d0..2eecf939 100644 --- a/src/drivers/logitech/onboard-profiles.test.ts +++ b/src/drivers/logitech/onboard-profiles.test.ts @@ -1025,6 +1025,12 @@ test("DPI slot limits are per format, not global", () => { assert.equal(clampDpi(Number.NaN, narrow), 200); }); +test("a legacy format accepts a slot whose lift-off was copied from another slot (G305 slot 3)", () => { + const limits = capabilitiesForFormat(3).dpiStages; + const stages = [800, 1200, 1600].map((dpi) => ({ x: dpi, y: dpi, lod: 2 })); + assert.equal(validateDpiStagePlan({ stages, defaultIndex: 0 }, limits, true), null); +}); + test("unwritable DPI stage plans are rejected before they reach flash", () => { const limits = capabilitiesForFormat(7).dpiStages; const stage = { x: 800, y: 800, lod: 1 }; diff --git a/src/drivers/logitech/onboard-profiles.ts b/src/drivers/logitech/onboard-profiles.ts index 295a239c..8cc952d6 100644 --- a/src/drivers/logitech/onboard-profiles.ts +++ b/src/drivers/logitech/onboard-profiles.ts @@ -1152,7 +1152,7 @@ export function validateDpiStagePlan( } if (legacyScalar) { if (stage.x !== stage.y) return `Slot ${index + 1} must use the same X and Y DPI.`; - if (stage.lod !== 0) return `Slot ${index + 1} cannot store lift-off distance.`; + // Lift-off is not stored here and the writer ignores it, so UI defaults must not fail the flash. } else if (!Number.isInteger(stage.lod) || stage.lod < 1 || stage.lod > 3) { // 0 is the "unused stage" marker on formats with per-stage lift-off. return `Slot ${index + 1} has an invalid lift-off level.`;