@kelnishi/satmouse-client 0.9.14 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -40,6 +40,12 @@ interface DeviceInfo$1 {
40
40
  productId?: number;
41
41
  connectionType?: "usb" | "wireless" | "bluetooth" | "unknown";
42
42
  connected?: boolean;
43
+ /** Axes this device provides (e.g., ["tx","ty","tz","rx","ry","rz"] or ["tx","ty","rx","ry","tz+","rz+"]) */
44
+ axes?: string[];
45
+ /** Human-readable labels for axes (same order as axes array) */
46
+ axisLabels?: string[];
47
+ /** Number of buttons this device provides */
48
+ buttonCount?: number;
43
49
  }
44
50
  type ConnectionState = "disconnected" | "connecting" | "connected" | "failed";
45
51
  type TransportProtocol = "webtransport" | "websocket" | "none";
@@ -104,78 +110,42 @@ declare class SatMouseConnection extends TypedEmitter<SatMouseEvents> {
104
110
  private clearReconnect;
105
111
  }
106
112
 
107
- interface FlipConfig {
108
- tx: boolean;
109
- ty: boolean;
110
- tz: boolean;
111
- rx: boolean;
112
- ry: boolean;
113
- rz: boolean;
114
- }
115
- interface SensitivityConfig {
116
- translation: number;
117
- rotation: number;
118
- }
119
- /** Maps each input axis to an output axis. E.g., { tx: "tz", tz: "tx" } swaps X and Z translation. */
120
- type AxisMap = {
121
- tx: keyof Vec3;
122
- ty: keyof Vec3;
123
- tz: keyof Vec3;
124
- rx: keyof Vec3;
125
- ry: keyof Vec3;
126
- rz: keyof Vec3;
127
- };
128
-
129
- /** Input axis identifier */
130
- type InputAxis = "tx" | "ty" | "tz" | "rx" | "ry" | "rz";
131
- /** A single action binding — maps one input axis to a named output */
132
- interface ActionBinding {
133
- /** Which input axis drives this action */
113
+ /** Axis identifier — full or half */
114
+ type InputAxis = "tx" | "ty" | "tz" | "rx" | "ry" | "rz" | "tx+" | "ty+" | "tz+" | "rx+" | "ry+" | "rz+" | "tx-" | "ty-" | "tz-" | "rx-" | "ry-" | "rz-";
115
+ /** A single axis route — reads from source, writes to target */
116
+ interface AxisRoute {
134
117
  source: InputAxis;
135
- /** Scale multiplier (default: 1) */
136
- scale?: number;
137
- /** Invert the value (default: false) */
138
- invert?: boolean;
118
+ target: InputAxis;
119
+ /** Negate the value (default: false) */
120
+ flip?: boolean;
139
121
  }
140
- /**
141
- * ActionMap defines how raw 6DOF axes map to named output actions.
142
- *
143
- * Client apps declare the actions they support and how device axes
144
- * feed into them. Users can reassign axes via the settings UI.
145
- *
146
- * Default: 6 actions matching the 6 input axes (passthrough).
147
- */
148
- type ActionMap = Record<string, ActionBinding>;
149
- /** Result of applying an ActionMap to spatial data */
150
- type ActionValues = Record<string, number>;
151
122
 
152
- /** Per-device transform overrides. Any field left undefined inherits from global defaults. */
123
+ /** Per-device configuration */
153
124
  interface DeviceConfig {
154
- sensitivity?: Partial<SensitivityConfig>;
155
- flip?: Partial<FlipConfig>;
125
+ /** Axis routing — each entry maps a device input to an output with optional flip */
126
+ routes?: AxisRoute[];
127
+ /** Scale multiplier applied to all axes (default: 1) */
128
+ scale?: number;
129
+ /** Dead zone threshold (0-1). Values below this are zeroed. */
156
130
  deadZone?: number;
131
+ /** Only pass the strongest axis, zero all others */
157
132
  dominant?: boolean;
158
- axisRemap?: Partial<AxisMap>;
159
- actionMap?: ActionMap;
160
- lockPosition?: boolean;
161
- lockRotation?: boolean;
162
133
  }
134
+ /** Global configuration */
163
135
  interface InputConfig {
164
- /** Global defaults applied to all devices */
165
- sensitivity: SensitivityConfig;
166
- flip: FlipConfig;
136
+ /** Default axis routes (used when device has no override) */
137
+ routes: AxisRoute[];
138
+ /** Default scale */
139
+ scale: number;
140
+ /** Dead zone threshold */
167
141
  deadZone: number;
142
+ /** Dominant axis mode */
168
143
  dominant: boolean;
169
- axisRemap: AxisMap;
144
+ /** Lock translation to zero */
170
145
  lockPosition: boolean;
146
+ /** Lock rotation to zero */
171
147
  lockRotation: boolean;
172
- /** Action map maps input axes to named output actions. Default: passthrough. */
173
- actionMap: ActionMap;
174
- /**
175
- * Per-device overrides, keyed by device ID (e.g., "spacemouse-c635")
176
- * or device family pattern (e.g., "spacemouse-*", "hid-054c-*").
177
- * Values override global defaults for matching devices.
178
- */
148
+ /** Per-device overrides keyed by device ID or pattern (e.g., "cnx-*") */
179
149
  devices: Record<string, DeviceConfig>;
180
150
  }
181
151
 
@@ -185,36 +155,17 @@ interface StorageAdapter {
185
155
  }
186
156
 
187
157
  interface InputManagerEvents {
188
- /** Processed spatial data (after all transforms + action map) */
189
158
  spatialData: (data: SpatialData) => void;
190
- /** Named action values from the action map */
191
- actionValues: (values: ActionValues) => void;
192
- /** Raw spatial data (before transforms) */
193
159
  rawSpatialData: (data: SpatialData) => void;
194
- /** Button event (pass-through from connection) */
195
160
  buttonEvent: (data: ButtonEvent) => void;
196
- /** Connection state changed */
197
161
  stateChange: (state: ConnectionState, protocol: TransportProtocol) => void;
198
- /** Device connected/disconnected */
199
162
  deviceStatus: (event: "connected" | "disconnected", device: DeviceInfo$1) => void;
200
- /** Configuration changed */
201
163
  configChange: (config: InputConfig) => void;
202
164
  }
203
- /** A connected device paired with its resolved configuration */
204
165
  interface DeviceWithConfig {
205
166
  device: DeviceInfo$1;
206
167
  config: DeviceConfig;
207
168
  }
208
- /**
209
- * Unified device service that wraps one or more SatMouseConnections
210
- * and provides a single processed event stream.
211
- *
212
- * Applies a configurable transform pipeline per-device:
213
- * deadZone → dominant → flip → axisRemap → sensitivity → lock
214
- *
215
- * Per-device overrides are resolved from InputConfig.devices using
216
- * device ID matching (exact or pattern with wildcard "*").
217
- */
218
169
  declare class InputManager extends TypedEmitter<InputManagerEvents> {
219
170
  private connections;
220
171
  private storage?;
@@ -225,36 +176,25 @@ declare class InputManager extends TypedEmitter<InputManagerEvents> {
225
176
  private _config;
226
177
  get config(): InputConfig;
227
178
  constructor(config?: Partial<InputConfig>, storage?: StorageAdapter);
228
- /** Add a connection to the managed set */
229
179
  addConnection(connection: SatMouseConnection): void;
230
- /** Remove a connection */
231
180
  removeConnection(connection: SatMouseConnection): void;
232
- /** Connect all managed connections */
233
181
  connect(): Promise<void>;
234
- /** Disconnect all managed connections */
235
182
  disconnect(): void;
236
- /** Fetch device info from all connections */
237
183
  fetchDeviceInfo(): Promise<DeviceInfo$1[]>;
238
- /** Get all known connected devices paired with their resolved config */
239
184
  getDevicesWithConfig(): DeviceWithConfig[];
240
- /** Get the resolved per-device config (global defaults + device overrides) */
241
185
  getDeviceConfig(deviceId: string): DeviceConfig;
242
- /** Update global configuration. Persists by default. */
243
186
  updateConfig(partial: Partial<InputConfig>, persist?: boolean): void;
244
- /** Update configuration for a specific device. Persists by default. */
245
187
  updateDeviceConfig(deviceId: string, partial: DeviceConfig, persist?: boolean): void;
246
- /** Register a callback for processed spatial data. Returns unsubscribe function. */
188
+ resetDeviceConfig(deviceId: string, persist?: boolean): void;
189
+ resetAllConfig(): void;
247
190
  onSpatialData(callback: (data: SpatialData) => void): () => void;
248
- /** Register a callback for button events. Returns unsubscribe function. */
249
191
  onButtonEvent(callback: (data: ButtonEvent) => void): () => void;
250
- /** Register a callback for action values. Returns unsubscribe function. */
251
- onActionValues(callback: (values: ActionValues) => void): () => void;
252
192
  private wireConnection;
253
193
  private flushAccumulator;
254
- /** Per-device transforms: flip, sensitivity, dead zone, dominant, axis remap */
194
+ /** Per-device: deadZone dominant routes (flip + scale + remap in one pass) */
255
195
  private processPerDevice;
256
- /** Global transforms applied after per-device merge: locks + action map */
257
- private applyGlobalTransforms;
196
+ /** Get the effective routes for a device: device config override > device axes metadata > global default */
197
+ private resolveRoutes;
258
198
  }
259
199
 
260
200
  interface SatMouseContextValue {
@@ -40,6 +40,12 @@ interface DeviceInfo$1 {
40
40
  productId?: number;
41
41
  connectionType?: "usb" | "wireless" | "bluetooth" | "unknown";
42
42
  connected?: boolean;
43
+ /** Axes this device provides (e.g., ["tx","ty","tz","rx","ry","rz"] or ["tx","ty","rx","ry","tz+","rz+"]) */
44
+ axes?: string[];
45
+ /** Human-readable labels for axes (same order as axes array) */
46
+ axisLabels?: string[];
47
+ /** Number of buttons this device provides */
48
+ buttonCount?: number;
43
49
  }
44
50
  type ConnectionState = "disconnected" | "connecting" | "connected" | "failed";
45
51
  type TransportProtocol = "webtransport" | "websocket" | "none";
@@ -104,78 +110,42 @@ declare class SatMouseConnection extends TypedEmitter<SatMouseEvents> {
104
110
  private clearReconnect;
105
111
  }
106
112
 
107
- interface FlipConfig {
108
- tx: boolean;
109
- ty: boolean;
110
- tz: boolean;
111
- rx: boolean;
112
- ry: boolean;
113
- rz: boolean;
114
- }
115
- interface SensitivityConfig {
116
- translation: number;
117
- rotation: number;
118
- }
119
- /** Maps each input axis to an output axis. E.g., { tx: "tz", tz: "tx" } swaps X and Z translation. */
120
- type AxisMap = {
121
- tx: keyof Vec3;
122
- ty: keyof Vec3;
123
- tz: keyof Vec3;
124
- rx: keyof Vec3;
125
- ry: keyof Vec3;
126
- rz: keyof Vec3;
127
- };
128
-
129
- /** Input axis identifier */
130
- type InputAxis = "tx" | "ty" | "tz" | "rx" | "ry" | "rz";
131
- /** A single action binding — maps one input axis to a named output */
132
- interface ActionBinding {
133
- /** Which input axis drives this action */
113
+ /** Axis identifier — full or half */
114
+ type InputAxis = "tx" | "ty" | "tz" | "rx" | "ry" | "rz" | "tx+" | "ty+" | "tz+" | "rx+" | "ry+" | "rz+" | "tx-" | "ty-" | "tz-" | "rx-" | "ry-" | "rz-";
115
+ /** A single axis route — reads from source, writes to target */
116
+ interface AxisRoute {
134
117
  source: InputAxis;
135
- /** Scale multiplier (default: 1) */
136
- scale?: number;
137
- /** Invert the value (default: false) */
138
- invert?: boolean;
118
+ target: InputAxis;
119
+ /** Negate the value (default: false) */
120
+ flip?: boolean;
139
121
  }
140
- /**
141
- * ActionMap defines how raw 6DOF axes map to named output actions.
142
- *
143
- * Client apps declare the actions they support and how device axes
144
- * feed into them. Users can reassign axes via the settings UI.
145
- *
146
- * Default: 6 actions matching the 6 input axes (passthrough).
147
- */
148
- type ActionMap = Record<string, ActionBinding>;
149
- /** Result of applying an ActionMap to spatial data */
150
- type ActionValues = Record<string, number>;
151
122
 
152
- /** Per-device transform overrides. Any field left undefined inherits from global defaults. */
123
+ /** Per-device configuration */
153
124
  interface DeviceConfig {
154
- sensitivity?: Partial<SensitivityConfig>;
155
- flip?: Partial<FlipConfig>;
125
+ /** Axis routing — each entry maps a device input to an output with optional flip */
126
+ routes?: AxisRoute[];
127
+ /** Scale multiplier applied to all axes (default: 1) */
128
+ scale?: number;
129
+ /** Dead zone threshold (0-1). Values below this are zeroed. */
156
130
  deadZone?: number;
131
+ /** Only pass the strongest axis, zero all others */
157
132
  dominant?: boolean;
158
- axisRemap?: Partial<AxisMap>;
159
- actionMap?: ActionMap;
160
- lockPosition?: boolean;
161
- lockRotation?: boolean;
162
133
  }
134
+ /** Global configuration */
163
135
  interface InputConfig {
164
- /** Global defaults applied to all devices */
165
- sensitivity: SensitivityConfig;
166
- flip: FlipConfig;
136
+ /** Default axis routes (used when device has no override) */
137
+ routes: AxisRoute[];
138
+ /** Default scale */
139
+ scale: number;
140
+ /** Dead zone threshold */
167
141
  deadZone: number;
142
+ /** Dominant axis mode */
168
143
  dominant: boolean;
169
- axisRemap: AxisMap;
144
+ /** Lock translation to zero */
170
145
  lockPosition: boolean;
146
+ /** Lock rotation to zero */
171
147
  lockRotation: boolean;
172
- /** Action map maps input axes to named output actions. Default: passthrough. */
173
- actionMap: ActionMap;
174
- /**
175
- * Per-device overrides, keyed by device ID (e.g., "spacemouse-c635")
176
- * or device family pattern (e.g., "spacemouse-*", "hid-054c-*").
177
- * Values override global defaults for matching devices.
178
- */
148
+ /** Per-device overrides keyed by device ID or pattern (e.g., "cnx-*") */
179
149
  devices: Record<string, DeviceConfig>;
180
150
  }
181
151
 
@@ -185,36 +155,17 @@ interface StorageAdapter {
185
155
  }
186
156
 
187
157
  interface InputManagerEvents {
188
- /** Processed spatial data (after all transforms + action map) */
189
158
  spatialData: (data: SpatialData) => void;
190
- /** Named action values from the action map */
191
- actionValues: (values: ActionValues) => void;
192
- /** Raw spatial data (before transforms) */
193
159
  rawSpatialData: (data: SpatialData) => void;
194
- /** Button event (pass-through from connection) */
195
160
  buttonEvent: (data: ButtonEvent) => void;
196
- /** Connection state changed */
197
161
  stateChange: (state: ConnectionState, protocol: TransportProtocol) => void;
198
- /** Device connected/disconnected */
199
162
  deviceStatus: (event: "connected" | "disconnected", device: DeviceInfo$1) => void;
200
- /** Configuration changed */
201
163
  configChange: (config: InputConfig) => void;
202
164
  }
203
- /** A connected device paired with its resolved configuration */
204
165
  interface DeviceWithConfig {
205
166
  device: DeviceInfo$1;
206
167
  config: DeviceConfig;
207
168
  }
208
- /**
209
- * Unified device service that wraps one or more SatMouseConnections
210
- * and provides a single processed event stream.
211
- *
212
- * Applies a configurable transform pipeline per-device:
213
- * deadZone → dominant → flip → axisRemap → sensitivity → lock
214
- *
215
- * Per-device overrides are resolved from InputConfig.devices using
216
- * device ID matching (exact or pattern with wildcard "*").
217
- */
218
169
  declare class InputManager extends TypedEmitter<InputManagerEvents> {
219
170
  private connections;
220
171
  private storage?;
@@ -225,36 +176,25 @@ declare class InputManager extends TypedEmitter<InputManagerEvents> {
225
176
  private _config;
226
177
  get config(): InputConfig;
227
178
  constructor(config?: Partial<InputConfig>, storage?: StorageAdapter);
228
- /** Add a connection to the managed set */
229
179
  addConnection(connection: SatMouseConnection): void;
230
- /** Remove a connection */
231
180
  removeConnection(connection: SatMouseConnection): void;
232
- /** Connect all managed connections */
233
181
  connect(): Promise<void>;
234
- /** Disconnect all managed connections */
235
182
  disconnect(): void;
236
- /** Fetch device info from all connections */
237
183
  fetchDeviceInfo(): Promise<DeviceInfo$1[]>;
238
- /** Get all known connected devices paired with their resolved config */
239
184
  getDevicesWithConfig(): DeviceWithConfig[];
240
- /** Get the resolved per-device config (global defaults + device overrides) */
241
185
  getDeviceConfig(deviceId: string): DeviceConfig;
242
- /** Update global configuration. Persists by default. */
243
186
  updateConfig(partial: Partial<InputConfig>, persist?: boolean): void;
244
- /** Update configuration for a specific device. Persists by default. */
245
187
  updateDeviceConfig(deviceId: string, partial: DeviceConfig, persist?: boolean): void;
246
- /** Register a callback for processed spatial data. Returns unsubscribe function. */
188
+ resetDeviceConfig(deviceId: string, persist?: boolean): void;
189
+ resetAllConfig(): void;
247
190
  onSpatialData(callback: (data: SpatialData) => void): () => void;
248
- /** Register a callback for button events. Returns unsubscribe function. */
249
191
  onButtonEvent(callback: (data: ButtonEvent) => void): () => void;
250
- /** Register a callback for action values. Returns unsubscribe function. */
251
- onActionValues(callback: (values: ActionValues) => void): () => void;
252
192
  private wireConnection;
253
193
  private flushAccumulator;
254
- /** Per-device transforms: flip, sensitivity, dead zone, dominant, axis remap */
194
+ /** Per-device: deadZone dominant routes (flip + scale + remap in one pass) */
255
195
  private processPerDevice;
256
- /** Global transforms applied after per-device merge: locks + action map */
257
- private applyGlobalTransforms;
196
+ /** Get the effective routes for a device: device config override > device axes metadata > global default */
197
+ private resolveRoutes;
258
198
  }
259
199
 
260
200
  interface SatMouseContextValue {