diff --git a/src/app/cards/AtkCards.tsx b/src/app/cards/AtkCards.tsx
index 1f5b1200..9b0d5e0a 100644
--- a/src/app/cards/AtkCards.tsx
+++ b/src/app/cards/AtkCards.tsx
@@ -1,7 +1,7 @@
import { useEffect, useRef, useState, type ReactNode } from "react";
import * as control from "../../device/controller";
import type { ControlSnapshot } from "../../device/types";
-import { Segmented, SwitchRow } from "../ui";
+import { Segmented } from "../ui";
export function AtkProfileCard({ snapshot }: { snapshot: ControlSnapshot }): ReactNode {
const status = snapshot.status;
@@ -154,50 +154,6 @@ export function AtkSensorCard({ snapshot }: { snapshot: ControlSnapshot }): Reac
);
}
-/**
- * Sensor rotation, mirroring the vendor HUB Parameter section: on/off first,
- * then the angle grid and auto-calibration. Rotation writes touch sensor
- * calibration, which needs a USBPcap capture before it can be implemented,
- * so the controls stay disabled with an honest note until then.
- */
-export function AtkRotationCard({ snapshot }: { snapshot: ControlSnapshot }): ReactNode {
- if (snapshot.status?.atkSensorMode == null) return null;
- return (
-
-
-
- undefined}
- />
-
- Precise horizontal movement regardless of mouse grip style.
-
- undefined}
- />
-
- Auto calibration: slide left-right inside the vendor HUB box — samples are collected and the path is calibrated in real time. Rotation writes are locked pending a USB capture; use ATK HUB for now.
-
-
-
- );
-}
-
export function AtkDongleCard({ snapshot }: { snapshot: ControlSnapshot }): ReactNode {
const dongleLight = snapshot.status?.atkDongleLight;
if (dongleLight == null) return null;
diff --git a/src/app/cards/availability.ts b/src/app/cards/availability.ts
index d1a980be..0c288865 100644
--- a/src/app/cards/availability.ts
+++ b/src/app/cards/availability.ts
@@ -31,7 +31,6 @@ export interface CardAvailability {
atkReceiver: boolean;
atkF1Sensor: boolean;
atkF1Dongle: boolean;
- atkF1Rotation: boolean;
mxMasterButtons: boolean;
pulsarPro: boolean;
onboardProfiles: boolean;
@@ -72,7 +71,6 @@ const NOTHING: CardAvailability = {
atkReceiver: false,
atkF1Sensor: false,
atkF1Dongle: false,
- atkF1Rotation: false,
mxMasterButtons: false,
pulsarPro: false,
onboardProfiles: false,
@@ -181,7 +179,6 @@ export function cardAvailability(snapshot: ControlSnapshot): CardAvailability {
atkReceiver: status.atkReceiver !== undefined,
atkF1Sensor: status.atkSensorMode != null,
atkF1Dongle: status.atkDongleLight != null,
- atkF1Rotation: status.atkSensorMode != null,
mxMasterButtons: traits.logitech && (snapshot.buttons?.length ?? 0) > 0,
pulsarPro: host && isPulsarProProtocol(status),
};
From f955f1530e40efabd0c59b69cdf9231923316fb5 Mon Sep 17 00:00:00 2001
From: NoxLoveYa <83467468+NoxLoveYa@users.noreply.github.com>
Date: Tue, 22 Sep 2026 23:53:37 +0200
Subject: [PATCH 09/16] feat(atk): stage F1 settings through footer Apply bar
---
src/device/controller.ts | 90 +++++++++++++++++++++-------------------
1 file changed, 48 insertions(+), 42 deletions(-)
diff --git a/src/device/controller.ts b/src/device/controller.ts
index d515033e..6c86dd66 100644
--- a/src/device/controller.ts
+++ b/src/device/controller.ts
@@ -1303,53 +1303,59 @@ export async function selectAtkR1Profile(profile: number): Promise
{
}
/**
- * Immediate ATK F1 Ultimate writes (sensor mode, anti-mistouch, dongle
- * light). Unlike staged slider changes these apply at once with a read-back,
- * following selectAtkR1Profile above.
+ * ATK F1 Ultimate settings (sensor mode, anti-mistouch, dongle light) use the
+ * shared staged flow: edits preview instantly and join the footer Apply bar
+ * like every other control.
*/
-async function writeAtkF1Setting(
- label: string,
- apply: (client: AtkHidClient) => Promise,
-): Promise {
- const client = activeAs(AtkHidClient);
- if (!client || refreshInProgress || settingInProgress) return;
- if (hasPendingChanges()) {
- setReadStatus(st("ctl.atkPending"));
- emit();
- return;
- }
- const device = activeDevice;
- settingInProgress = true;
- setReadStatus(label);
- emit();
- recordDiagnosticCommand(label);
- try {
- await apply(client);
- if (active !== client || activeDevice !== device) return;
- const status = await statusAfterWrite(client);
- if (active !== client || activeDevice !== device) return;
- applyStatus(status);
- setReadStatus(label);
- } catch (error) {
- if (active !== client || activeDevice !== device) return;
- recordDiagnosticError(error, label);
- setReadStatus(error instanceof Error ? error.message : label);
- } finally {
- settingInProgress = false;
- emit();
- }
-}
-
-export async function selectAtkSensorMode(mode: number): Promise {
- await writeAtkF1Setting(`Set ATK sensor mode to ${mode}`, (client) => client.setAtkSensorMode(mode));
+export function selectAtkSensorMode(mode: number): void {
+ if (!hasActiveClient()) return;
+ if (!Number.isInteger(mode) || mode < 0 || mode > 2) return;
+ stageChange({
+ key: "atk-sensor-mode",
+ label: `Sensor mode ${["Basic", "Shard", "MAX"][mode] ?? mode}`,
+ command: `Set ATK sensor mode to ${mode}`,
+ progress: `Setting ATK sensor mode…`,
+ preview: (status) => {
+ status.atkSensorMode = mode;
+ },
+ apply: async () => {
+ await requireClientMethod("setAtkSensorMode", "the sensor mode").setAtkSensorMode(mode);
+ },
+ });
}
-export async function applyAtkAntiMistouch(milliseconds: number): Promise {
- await writeAtkF1Setting(`Set ATK anti-mistouch to ${milliseconds} ms`, (client) => client.setAntiMistouchMs(milliseconds));
+export function applyAtkAntiMistouch(milliseconds: number): void {
+ if (!hasActiveClient()) return;
+ if (!Number.isInteger(milliseconds) || milliseconds < 0 || milliseconds > 2550) return;
+ stageChange({
+ key: "atk-anti-mistouch",
+ label: milliseconds === 0 ? "Anti-mistouch off" : `Anti-mistouch ${milliseconds} ms`,
+ command: `Set ATK anti-mistouch to ${milliseconds} ms`,
+ progress: `Setting ATK anti-mistouch…`,
+ preview: (status) => {
+ status.atkAntiMistouchMs = milliseconds;
+ },
+ apply: async () => {
+ await requireClientMethod("setAntiMistouchMs", "anti-mistouch").setAntiMistouchMs(milliseconds);
+ },
+ });
}
-export async function selectAtkDongleLight(mode: number): Promise {
- await writeAtkF1Setting(`Set ATK dongle light to ${mode}`, (client) => client.setDongleLight(mode));
+export function selectAtkDongleLight(mode: number): void {
+ if (!hasActiveClient()) return;
+ if (!Number.isInteger(mode) || mode < 0 || mode > 3) return;
+ stageChange({
+ key: "atk-dongle-light",
+ label: `Dongle light ${["Off", "Polling", "Battery", "Low battery"][mode] ?? mode}`,
+ command: `Set ATK dongle light to ${mode}`,
+ progress: `Setting ATK dongle light…`,
+ preview: (status) => {
+ status.atkDongleLight = mode;
+ },
+ apply: async () => {
+ await requireClientMethod("setDongleLight", "the dongle light").setDongleLight(mode);
+ },
+ });
}
export async function pairAtkR1SePlusReceiver(): Promise {
From 429ec7e26ad0f9ff0ae2b94707ad6c3477990f59 Mon Sep 17 00:00:00 2001
From: NoxLoveYa <83467468+NoxLoveYa@users.noreply.github.com>
Date: Tue, 22 Sep 2026 23:56:59 +0200
Subject: [PATCH 10/16] fix(atk): three-column sensor segmented, stable note
height
---
src/app/cards/AtkCards.tsx | 17 +++++++++--------
src/control.css | 1 +
2 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/src/app/cards/AtkCards.tsx b/src/app/cards/AtkCards.tsx
index 9b0d5e0a..d1094025 100644
--- a/src/app/cards/AtkCards.tsx
+++ b/src/app/cards/AtkCards.tsx
@@ -135,14 +135,15 @@ export function AtkSensorCard({ snapshot }: { snapshot: ControlSnapshot }): Reac
PERFORMANCE
Sensor Sampling Rate