diff --git a/build/check-bundle-size.ts b/build/check-bundle-size.ts index bd12f69f..80f80a0c 100644 --- a/build/check-bundle-size.ts +++ b/build/check-bundle-size.ts @@ -7,9 +7,9 @@ const BUDGET_BYTES: Record = { ".css": 46_000, // Raised from 280 kB for the production Logitech onboard-profile codec, // guarded flash editor, verification exporter, upstream Finalmouse driver, - // the dedicated Viper Mini protocol driver, and Viper V3 sleep/low-power - // protocol support. Preview fixtures remain dev-only. - ".js": 320_000, + // the dedicated Viper Mini protocol driver, and Viper V3 sleep/low-power plus + // asymmetric lift-off protocol and controls. Preview fixtures remain dev-only. + ".js": 325_000, }; const ASSETS = join("dist", "assets"); diff --git a/package-lock.json b/package-lock.json index 2985c3aa..52f56e8a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -887,9 +887,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.16", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", - "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "dev": true, "funding": [ { diff --git a/src/control-events.ts b/src/control-events.ts index ecc74e21..658a1825 100644 --- a/src/control-events.ts +++ b/src/control-events.ts @@ -49,6 +49,9 @@ export interface ControlEventHandlers { applyFinalmouseSetting(setting: "dongleLed" | "tournamentScroll" | "tournamentTimeout", value: number): void; applyPollingRate(rate: number): void; applyLiftOffDistance(lod: NonNullable): void; + applyLiftOffMode(mode: "single" | "asymmetric"): void; + applyAsymmetricLiftOff(liftOff: number, landing: number): void; + capLandingToLiftOff(): void; applyGamingSurfaceMode(mode: NonNullable): void; applyLightforceSwitchMode(mode: NonNullable): void; // Mode and profile selection apply immediately: both are volatile navigation @@ -226,6 +229,25 @@ export function bindControlEvents(handlers: ControlEventHandlers): void { if (lod) void handlers.applyLiftOffDistance(lod); }); }); + document.querySelectorAll("[data-lod-mode]").forEach((button) => { + button.addEventListener("click", () => { + const mode = button.dataset.lodMode; + if (mode === "single" || mode === "asymmetric") handlers.applyLiftOffMode(mode); + }); + }); + const liftOffSlider = document.querySelector("#lod-lift-off"); + const landingSlider = document.querySelector("#lod-landing"); + for (const slider of [liftOffSlider, landingSlider]) { + // `input` keeps the readout and the landing ceiling honest while dragging; + // `change` fires on release, so a drag stages one change rather than thirty. + slider?.addEventListener("input", () => handlers.capLandingToLiftOff()); + slider?.addEventListener("change", () => { + handlers.capLandingToLiftOff(); + const liftOff = Number(liftOffSlider?.value); + const landing = Number(landingSlider?.value); + if (Number.isFinite(liftOff) && Number.isFinite(landing)) handlers.applyAsymmetricLiftOff(liftOff, landing); + }); + } document.querySelectorAll("[data-gaming-surface]").forEach((button) => { button.addEventListener("click", () => { const mode = button.dataset.gamingSurface as MouseStatus["gamingSurfaceMode"]; diff --git a/src/control-template.ts b/src/control-template.ts index 268bcce6..417bfb8b 100644 --- a/src/control-template.ts +++ b/src/control-template.ts @@ -76,7 +76,7 @@ export function controlTemplate(buildLabel: string): string {

DPI

Sensitivity

Choose a DPI value

POLLING RATE

Report frequency

Higher rates update cursor movement more often, but use more battery.
-

SENSOR

Lift-off distance

Controls how far you can lift the mouse before tracking stops. Higher values keep tracking a little longer.
+

SENSOR

Lift-off distance

Controls how far you can lift the mouse before tracking stops. Higher values keep tracking a little longer.