@kelnishi/satmouse-client 0.9.3 → 0.9.5
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.
- package/dist/react/index.cjs +150 -9
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +64 -4
- package/dist/react/index.d.ts +64 -4
- package/dist/react/index.js +150 -9
- package/dist/react/index.js.map +1 -1
- package/dist/utils/index.cjs +165 -9
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.d.cts +79 -6
- package/dist/utils/index.d.ts +79 -6
- package/dist/utils/index.js +161 -10
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
package/dist/utils/index.cjs
CHANGED
|
@@ -2,6 +2,67 @@
|
|
|
2
2
|
|
|
3
3
|
var chunkI5MEZZOT_cjs = require('../chunk-I5MEZZOT.cjs');
|
|
4
4
|
|
|
5
|
+
// src/utils/action-map.ts
|
|
6
|
+
var DEFAULT_ACTION_MAP = {
|
|
7
|
+
tx: { source: "tx" },
|
|
8
|
+
ty: { source: "ty" },
|
|
9
|
+
tz: { source: "tz" },
|
|
10
|
+
rx: { source: "rx" },
|
|
11
|
+
ry: { source: "ry" },
|
|
12
|
+
rz: { source: "rz" }
|
|
13
|
+
};
|
|
14
|
+
function readAxis(data, axis) {
|
|
15
|
+
switch (axis) {
|
|
16
|
+
case "tx":
|
|
17
|
+
return data.translation.x;
|
|
18
|
+
case "ty":
|
|
19
|
+
return data.translation.y;
|
|
20
|
+
case "tz":
|
|
21
|
+
return data.translation.z;
|
|
22
|
+
case "rx":
|
|
23
|
+
return data.rotation.x;
|
|
24
|
+
case "ry":
|
|
25
|
+
return data.rotation.y;
|
|
26
|
+
case "rz":
|
|
27
|
+
return data.rotation.z;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function applyActionMap(data, map) {
|
|
31
|
+
const result = {};
|
|
32
|
+
for (const [action, binding] of Object.entries(map)) {
|
|
33
|
+
let value = readAxis(data, binding.source);
|
|
34
|
+
if (binding.invert) value = -value;
|
|
35
|
+
value *= binding.scale ?? 1;
|
|
36
|
+
result[action] = value;
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
function actionValuesToSpatialData(values, timestamp) {
|
|
41
|
+
return {
|
|
42
|
+
translation: {
|
|
43
|
+
x: values.tx ?? 0,
|
|
44
|
+
y: values.ty ?? 0,
|
|
45
|
+
z: values.tz ?? 0
|
|
46
|
+
},
|
|
47
|
+
rotation: {
|
|
48
|
+
x: values.rx ?? 0,
|
|
49
|
+
y: values.ry ?? 0,
|
|
50
|
+
z: values.rz ?? 0
|
|
51
|
+
},
|
|
52
|
+
timestamp
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function swapActions(map, actionA, actionB) {
|
|
56
|
+
const result = { ...map };
|
|
57
|
+
const a = result[actionA];
|
|
58
|
+
const b = result[actionB];
|
|
59
|
+
if (a && b) {
|
|
60
|
+
result[actionA] = b;
|
|
61
|
+
result[actionB] = a;
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
|
|
5
66
|
// src/utils/config.ts
|
|
6
67
|
var DEFAULT_CONFIG = {
|
|
7
68
|
sensitivity: { translation: 1e-3, rotation: 1e-3 },
|
|
@@ -10,15 +71,60 @@ var DEFAULT_CONFIG = {
|
|
|
10
71
|
dominant: false,
|
|
11
72
|
axisRemap: { tx: "x", ty: "y", tz: "z", rx: "x", ry: "y", rz: "z" },
|
|
12
73
|
lockPosition: false,
|
|
13
|
-
lockRotation: false
|
|
74
|
+
lockRotation: false,
|
|
75
|
+
actionMap: { ...DEFAULT_ACTION_MAP },
|
|
76
|
+
devices: {}
|
|
14
77
|
};
|
|
15
78
|
function mergeConfig(base, partial) {
|
|
16
|
-
|
|
79
|
+
const merged = {
|
|
17
80
|
...base,
|
|
18
81
|
...partial,
|
|
19
82
|
sensitivity: { ...base.sensitivity, ...partial.sensitivity },
|
|
20
83
|
flip: { ...base.flip, ...partial.flip },
|
|
21
|
-
axisRemap: { ...base.axisRemap, ...partial.axisRemap }
|
|
84
|
+
axisRemap: { ...base.axisRemap, ...partial.axisRemap },
|
|
85
|
+
actionMap: partial.actionMap ? { ...base.actionMap, ...partial.actionMap } : { ...base.actionMap },
|
|
86
|
+
devices: { ...base.devices }
|
|
87
|
+
};
|
|
88
|
+
if (partial.devices) {
|
|
89
|
+
for (const [key, devCfg] of Object.entries(partial.devices)) {
|
|
90
|
+
merged.devices[key] = mergeDeviceConfig(merged.devices[key], devCfg);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return merged;
|
|
94
|
+
}
|
|
95
|
+
function mergeDeviceConfig(base, partial) {
|
|
96
|
+
if (!base) return partial;
|
|
97
|
+
return {
|
|
98
|
+
...base,
|
|
99
|
+
...partial,
|
|
100
|
+
sensitivity: partial.sensitivity ? { ...base.sensitivity, ...partial.sensitivity } : base.sensitivity,
|
|
101
|
+
flip: partial.flip ? { ...base.flip, ...partial.flip } : base.flip,
|
|
102
|
+
axisRemap: partial.axisRemap ? { ...base.axisRemap, ...partial.axisRemap } : base.axisRemap
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function resolveDeviceConfig(config, deviceId) {
|
|
106
|
+
let deviceOverride;
|
|
107
|
+
if (config.devices[deviceId]) {
|
|
108
|
+
deviceOverride = config.devices[deviceId];
|
|
109
|
+
} else {
|
|
110
|
+
for (const [pattern, cfg] of Object.entries(config.devices)) {
|
|
111
|
+
if (pattern.endsWith("*") && deviceId.startsWith(pattern.slice(0, -1))) {
|
|
112
|
+
deviceOverride = cfg;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (!deviceOverride) return config;
|
|
118
|
+
return {
|
|
119
|
+
...config,
|
|
120
|
+
sensitivity: { ...config.sensitivity, ...deviceOverride.sensitivity },
|
|
121
|
+
flip: { ...config.flip, ...deviceOverride.flip },
|
|
122
|
+
deadZone: deviceOverride.deadZone ?? config.deadZone,
|
|
123
|
+
dominant: deviceOverride.dominant ?? config.dominant,
|
|
124
|
+
axisRemap: { ...config.axisRemap, ...deviceOverride.axisRemap },
|
|
125
|
+
actionMap: deviceOverride.actionMap ? { ...config.actionMap, ...deviceOverride.actionMap } : config.actionMap,
|
|
126
|
+
lockPosition: deviceOverride.lockPosition ?? config.lockPosition,
|
|
127
|
+
lockRotation: deviceOverride.lockRotation ?? config.lockRotation
|
|
22
128
|
};
|
|
23
129
|
}
|
|
24
130
|
|
|
@@ -138,6 +244,7 @@ function applyAxisRemap(data, map) {
|
|
|
138
244
|
var InputManager = class extends chunkI5MEZZOT_cjs.TypedEmitter {
|
|
139
245
|
connections = [];
|
|
140
246
|
storage;
|
|
247
|
+
knownDevices = /* @__PURE__ */ new Map();
|
|
141
248
|
_config;
|
|
142
249
|
get config() {
|
|
143
250
|
return this._config;
|
|
@@ -170,14 +277,46 @@ var InputManager = class extends chunkI5MEZZOT_cjs.TypedEmitter {
|
|
|
170
277
|
/** Fetch device info from all connections */
|
|
171
278
|
async fetchDeviceInfo() {
|
|
172
279
|
const results = await Promise.all(this.connections.map((c) => c.fetchDeviceInfo()));
|
|
173
|
-
|
|
280
|
+
const devices = results.flat();
|
|
281
|
+
for (const d of devices) this.knownDevices.set(d.id, d);
|
|
282
|
+
return devices;
|
|
283
|
+
}
|
|
284
|
+
/** Get all known connected devices paired with their resolved config */
|
|
285
|
+
getDevicesWithConfig() {
|
|
286
|
+
return Array.from(this.knownDevices.values()).map((device) => ({
|
|
287
|
+
device,
|
|
288
|
+
config: this.getDeviceConfig(device.id)
|
|
289
|
+
}));
|
|
290
|
+
}
|
|
291
|
+
/** Get the resolved per-device config (global defaults + device overrides) */
|
|
292
|
+
getDeviceConfig(deviceId) {
|
|
293
|
+
const resolved = resolveDeviceConfig(this._config, deviceId);
|
|
294
|
+
return {
|
|
295
|
+
sensitivity: resolved.sensitivity,
|
|
296
|
+
flip: resolved.flip,
|
|
297
|
+
deadZone: resolved.deadZone,
|
|
298
|
+
dominant: resolved.dominant,
|
|
299
|
+
axisRemap: resolved.axisRemap,
|
|
300
|
+
actionMap: resolved.actionMap,
|
|
301
|
+
lockPosition: resolved.lockPosition,
|
|
302
|
+
lockRotation: resolved.lockRotation
|
|
303
|
+
};
|
|
174
304
|
}
|
|
175
|
-
/** Update configuration. Persists by default. */
|
|
305
|
+
/** Update global configuration. Persists by default. */
|
|
176
306
|
updateConfig(partial, persist = true) {
|
|
177
307
|
this._config = mergeConfig(this._config, partial);
|
|
178
308
|
if (persist) saveSettings(this._config, this.storage);
|
|
179
309
|
this.emit("configChange", this._config);
|
|
180
310
|
}
|
|
311
|
+
/** Update configuration for a specific device. Persists by default. */
|
|
312
|
+
updateDeviceConfig(deviceId, partial, persist = true) {
|
|
313
|
+
const existing = this._config.devices[deviceId] ?? {};
|
|
314
|
+
this._config = mergeConfig(this._config, {
|
|
315
|
+
devices: { [deviceId]: { ...existing, ...partial } }
|
|
316
|
+
});
|
|
317
|
+
if (persist) saveSettings(this._config, this.storage);
|
|
318
|
+
this.emit("configChange", this._config);
|
|
319
|
+
}
|
|
181
320
|
/** Register a callback for processed spatial data. Returns unsubscribe function. */
|
|
182
321
|
onSpatialData(callback) {
|
|
183
322
|
this.on("spatialData", callback);
|
|
@@ -188,15 +327,25 @@ var InputManager = class extends chunkI5MEZZOT_cjs.TypedEmitter {
|
|
|
188
327
|
this.on("buttonEvent", callback);
|
|
189
328
|
return () => this.off("buttonEvent", callback);
|
|
190
329
|
}
|
|
330
|
+
/** Register a callback for action values. Returns unsubscribe function. */
|
|
331
|
+
onActionValues(callback) {
|
|
332
|
+
this.on("actionValues", callback);
|
|
333
|
+
return () => this.off("actionValues", callback);
|
|
334
|
+
}
|
|
191
335
|
wireConnection(connection) {
|
|
192
336
|
connection.on("spatialData", (raw) => {
|
|
193
337
|
this.emit("rawSpatialData", raw);
|
|
194
|
-
const
|
|
195
|
-
if (
|
|
338
|
+
const { spatial, actions } = this.processSpatialData(raw);
|
|
339
|
+
if (spatial) this.emit("spatialData", spatial);
|
|
340
|
+
if (actions) this.emit("actionValues", actions);
|
|
196
341
|
});
|
|
197
342
|
connection.on("buttonEvent", (event) => this.emit("buttonEvent", event));
|
|
198
343
|
connection.on("stateChange", (state, proto) => this.emit("stateChange", state, proto));
|
|
199
|
-
connection.on("deviceStatus", (event, device) =>
|
|
344
|
+
connection.on("deviceStatus", (event, device) => {
|
|
345
|
+
if (event === "connected") this.knownDevices.set(device.id, device);
|
|
346
|
+
else this.knownDevices.delete(device.id);
|
|
347
|
+
this.emit("deviceStatus", event, device);
|
|
348
|
+
});
|
|
200
349
|
}
|
|
201
350
|
processSpatialData(raw) {
|
|
202
351
|
const cfg = this._config;
|
|
@@ -212,13 +361,18 @@ var InputManager = class extends chunkI5MEZZOT_cjs.TypedEmitter {
|
|
|
212
361
|
if (cfg.lockRotation) {
|
|
213
362
|
data = { ...data, rotation: { x: 0, y: 0, z: 0 } };
|
|
214
363
|
}
|
|
215
|
-
|
|
364
|
+
const actions = applyActionMap(data, cfg.actionMap);
|
|
365
|
+
const spatial = actionValuesToSpatialData(actions, data.timestamp);
|
|
366
|
+
return { spatial, actions };
|
|
216
367
|
}
|
|
217
368
|
};
|
|
218
369
|
|
|
370
|
+
exports.DEFAULT_ACTION_MAP = DEFAULT_ACTION_MAP;
|
|
219
371
|
exports.DEFAULT_AXIS_MAP = DEFAULT_AXIS_MAP;
|
|
220
372
|
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
|
221
373
|
exports.InputManager = InputManager;
|
|
374
|
+
exports.actionValuesToSpatialData = actionValuesToSpatialData;
|
|
375
|
+
exports.applyActionMap = applyActionMap;
|
|
222
376
|
exports.applyAxisRemap = applyAxisRemap;
|
|
223
377
|
exports.applyDeadZone = applyDeadZone;
|
|
224
378
|
exports.applyDominant = applyDominant;
|
|
@@ -226,6 +380,8 @@ exports.applyFlip = applyFlip;
|
|
|
226
380
|
exports.applySensitivity = applySensitivity;
|
|
227
381
|
exports.loadSettings = loadSettings;
|
|
228
382
|
exports.mergeConfig = mergeConfig;
|
|
383
|
+
exports.resolveDeviceConfig = resolveDeviceConfig;
|
|
229
384
|
exports.saveSettings = saveSettings;
|
|
385
|
+
exports.swapActions = swapActions;
|
|
230
386
|
//# sourceMappingURL=index.cjs.map
|
|
231
387
|
//# sourceMappingURL=index.cjs.map
|
package/dist/utils/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/config.ts","../../src/utils/persistence.ts","../../src/utils/transforms.ts","../../src/utils/input-manager.ts"],"names":["TypedEmitter"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { FlipConfig, SensitivityConfig, AxisMap } from \"./transforms.js\";\n\nexport interface InputConfig {\n sensitivity: SensitivityConfig;\n flip: FlipConfig;\n deadZone: number;\n dominant: boolean;\n axisRemap: AxisMap;\n lockPosition: boolean;\n lockRotation: boolean;\n}\n\nexport const DEFAULT_CONFIG: InputConfig = {\n sensitivity: { translation: 0.001, rotation: 0.001 },\n flip: { tx: false, ty: true, tz: true, rx: false, ry: true, rz: true },\n deadZone: 0,\n dominant: false,\n axisRemap: { tx: \"x\", ty: \"y\", tz: \"z\", rx: \"x\", ry: \"y\", rz: \"z\" },\n lockPosition: false,\n lockRotation: false,\n};\n\nexport function mergeConfig(base: InputConfig, partial: Partial<InputConfig>): InputConfig {\n return {\n ...base,\n ...partial,\n sensitivity: { ...base.sensitivity, ...partial.sensitivity },\n flip: { ...base.flip, ...partial.flip },\n axisRemap: { ...base.axisRemap, ...partial.axisRemap },\n };\n}\n","import type { InputConfig } from \"./config.js\";\n\nexport interface StorageAdapter {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n}\n\nconst STORAGE_KEY = \"satmouse:settings\";\n\nfunction getStorage(storage?: StorageAdapter): StorageAdapter | null {\n if (storage) return storage;\n try {\n return globalThis.localStorage ?? null;\n } catch {\n return null;\n }\n}\n\nexport function saveSettings(config: InputConfig, storage?: StorageAdapter): void {\n const s = getStorage(storage);\n if (!s) return;\n s.setItem(STORAGE_KEY, JSON.stringify(config));\n}\n\nexport function loadSettings(storage?: StorageAdapter): Partial<InputConfig> | null {\n const s = getStorage(storage);\n if (!s) return null;\n const raw = s.getItem(STORAGE_KEY);\n if (!raw) return null;\n try {\n return JSON.parse(raw) as Partial<InputConfig>;\n } catch {\n return null;\n }\n}\n","import type { SpatialData, Vec3 } from \"../core/types.js\";\n\nexport interface FlipConfig {\n tx: boolean;\n ty: boolean;\n tz: boolean;\n rx: boolean;\n ry: boolean;\n rz: boolean;\n}\n\nexport interface SensitivityConfig {\n translation: number;\n rotation: number;\n}\n\n/** Maps each input axis to an output axis. E.g., { tx: \"tz\", tz: \"tx\" } swaps X and Z translation. */\nexport type AxisMap = {\n tx: keyof Vec3;\n ty: keyof Vec3;\n tz: keyof Vec3;\n rx: keyof Vec3;\n ry: keyof Vec3;\n rz: keyof Vec3;\n};\n\nexport const DEFAULT_AXIS_MAP: AxisMap = {\n tx: \"x\", ty: \"y\", tz: \"z\",\n rx: \"x\", ry: \"y\", rz: \"z\",\n};\n\nexport function applyFlip(data: SpatialData, flip: FlipConfig): SpatialData {\n return {\n ...data,\n translation: {\n x: flip.tx ? -data.translation.x : data.translation.x,\n y: flip.ty ? -data.translation.y : data.translation.y,\n z: flip.tz ? -data.translation.z : data.translation.z,\n },\n rotation: {\n x: flip.rx ? -data.rotation.x : data.rotation.x,\n y: flip.ry ? -data.rotation.y : data.rotation.y,\n z: flip.rz ? -data.rotation.z : data.rotation.z,\n },\n };\n}\n\nexport function applySensitivity(data: SpatialData, sens: SensitivityConfig): SpatialData {\n return {\n ...data,\n translation: {\n x: data.translation.x * sens.translation,\n y: data.translation.y * sens.translation,\n z: data.translation.z * sens.translation,\n },\n rotation: {\n x: data.rotation.x * sens.rotation,\n y: data.rotation.y * sens.rotation,\n z: data.rotation.z * sens.rotation,\n },\n };\n}\n\nexport function applyDominant(data: SpatialData): SpatialData {\n const axes = [\n { group: \"t\" as const, key: \"x\" as const, v: Math.abs(data.translation.x) },\n { group: \"t\" as const, key: \"y\" as const, v: Math.abs(data.translation.y) },\n { group: \"t\" as const, key: \"z\" as const, v: Math.abs(data.translation.z) },\n { group: \"r\" as const, key: \"x\" as const, v: Math.abs(data.rotation.x) },\n { group: \"r\" as const, key: \"y\" as const, v: Math.abs(data.rotation.y) },\n { group: \"r\" as const, key: \"z\" as const, v: Math.abs(data.rotation.z) },\n ];\n const max = axes.reduce((a, b) => (b.v > a.v ? b : a));\n\n const t: Vec3 = { x: 0, y: 0, z: 0 };\n const r: Vec3 = { x: 0, y: 0, z: 0 };\n\n if (max.group === \"t\") t[max.key] = data.translation[max.key];\n else r[max.key] = data.rotation[max.key];\n\n return { ...data, translation: t, rotation: r };\n}\n\nexport function applyDeadZone(data: SpatialData, threshold: number): SpatialData {\n const dz = (v: number) => (Math.abs(v) < threshold ? 0 : v);\n return {\n ...data,\n translation: { x: dz(data.translation.x), y: dz(data.translation.y), z: dz(data.translation.z) },\n rotation: { x: dz(data.rotation.x), y: dz(data.rotation.y), z: dz(data.rotation.z) },\n };\n}\n\nexport function applyAxisRemap(data: SpatialData, map: AxisMap): SpatialData {\n return {\n ...data,\n translation: {\n x: 0, y: 0, z: 0,\n [map.tx]: data.translation.x,\n [map.ty]: data.translation.y,\n [map.tz]: data.translation.z,\n } as Vec3,\n rotation: {\n x: 0, y: 0, z: 0,\n [map.rx]: data.rotation.x,\n [map.ry]: data.rotation.y,\n [map.rz]: data.rotation.z,\n } as Vec3,\n };\n}\n","import { TypedEmitter } from \"../core/emitter.js\";\nimport type { SatMouseConnection } from \"../core/connection.js\";\nimport type { SpatialData, ButtonEvent, DeviceInfo, ConnectionState, TransportProtocol } from \"../core/types.js\";\nimport type { InputConfig } from \"./config.js\";\nimport { DEFAULT_CONFIG, mergeConfig } from \"./config.js\";\nimport { loadSettings, saveSettings, type StorageAdapter } from \"./persistence.js\";\nimport {\n applyFlip,\n applySensitivity,\n applyDominant,\n applyDeadZone,\n applyAxisRemap,\n} from \"./transforms.js\";\n\nexport interface InputManagerEvents {\n /** Processed spatial data (after all transforms) */\n spatialData: (data: SpatialData) => void;\n /** Raw spatial data (before transforms) */\n rawSpatialData: (data: SpatialData) => void;\n /** Button event (pass-through from connection) */\n buttonEvent: (data: ButtonEvent) => void;\n /** Connection state changed */\n stateChange: (state: ConnectionState, protocol: TransportProtocol) => void;\n /** Device connected/disconnected */\n deviceStatus: (event: \"connected\" | \"disconnected\", device: DeviceInfo) => void;\n /** Configuration changed */\n configChange: (config: InputConfig) => void;\n}\n\n/**\n * Unified device service that wraps one or more SatMouseConnections\n * and provides a single processed event stream.\n *\n * Applies a configurable transform pipeline to spatial data:\n * deadZone → dominant → flip → axisRemap → sensitivity → lock\n *\n * Persists settings to storage (localStorage by default).\n */\nexport class InputManager extends TypedEmitter<InputManagerEvents> {\n private connections: SatMouseConnection[] = [];\n private storage?: StorageAdapter;\n\n private _config: InputConfig;\n\n get config(): InputConfig {\n return this._config;\n }\n\n constructor(config?: Partial<InputConfig>, storage?: StorageAdapter) {\n super();\n this.storage = storage;\n const persisted = loadSettings(storage);\n this._config = mergeConfig(DEFAULT_CONFIG, { ...config, ...persisted });\n }\n\n /** Add a connection to the managed set */\n addConnection(connection: SatMouseConnection): void {\n this.connections.push(connection);\n this.wireConnection(connection);\n }\n\n /** Remove a connection */\n removeConnection(connection: SatMouseConnection): void {\n const idx = this.connections.indexOf(connection);\n if (idx !== -1) this.connections.splice(idx, 1);\n connection.removeAllListeners();\n }\n\n /** Connect all managed connections */\n async connect(): Promise<void> {\n await Promise.all(this.connections.map((c) => c.connect()));\n }\n\n /** Disconnect all managed connections */\n disconnect(): void {\n for (const c of this.connections) c.disconnect();\n }\n\n /** Fetch device info from all connections */\n async fetchDeviceInfo(): Promise<DeviceInfo[]> {\n const results = await Promise.all(this.connections.map((c) => c.fetchDeviceInfo()));\n return results.flat();\n }\n\n /** Update configuration. Persists by default. */\n updateConfig(partial: Partial<InputConfig>, persist = true): void {\n this._config = mergeConfig(this._config, partial);\n if (persist) saveSettings(this._config, this.storage);\n this.emit(\"configChange\", this._config);\n }\n\n /** Register a callback for processed spatial data. Returns unsubscribe function. */\n onSpatialData(callback: (data: SpatialData) => void): () => void {\n this.on(\"spatialData\", callback);\n return () => this.off(\"spatialData\", callback);\n }\n\n /** Register a callback for button events. Returns unsubscribe function. */\n onButtonEvent(callback: (data: ButtonEvent) => void): () => void {\n this.on(\"buttonEvent\", callback);\n return () => this.off(\"buttonEvent\", callback);\n }\n\n private wireConnection(connection: SatMouseConnection): void {\n connection.on(\"spatialData\", (raw) => {\n this.emit(\"rawSpatialData\", raw);\n const processed = this.processSpatialData(raw);\n if (processed) this.emit(\"spatialData\", processed);\n });\n\n connection.on(\"buttonEvent\", (event) => this.emit(\"buttonEvent\", event));\n connection.on(\"stateChange\", (state, proto) => this.emit(\"stateChange\", state, proto));\n connection.on(\"deviceStatus\", (event, device) => this.emit(\"deviceStatus\", event, device));\n }\n\n private processSpatialData(raw: SpatialData): SpatialData | null {\n const cfg = this._config;\n let data = raw;\n\n // Transform pipeline\n if (cfg.deadZone > 0) data = applyDeadZone(data, cfg.deadZone);\n if (cfg.dominant) data = applyDominant(data);\n data = applyFlip(data, cfg.flip);\n data = applyAxisRemap(data, cfg.axisRemap);\n data = applySensitivity(data, cfg.sensitivity);\n\n // Lock axes\n if (cfg.lockPosition) {\n data = { ...data, translation: { x: 0, y: 0, z: 0 } };\n }\n if (cfg.lockRotation) {\n data = { ...data, rotation: { x: 0, y: 0, z: 0 } };\n }\n\n return data;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/action-map.ts","../../src/utils/config.ts","../../src/utils/persistence.ts","../../src/utils/transforms.ts","../../src/utils/input-manager.ts"],"names":["TypedEmitter"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { SpatialData } from \"../core/types.js\";\n\n/** Input axis identifier */\nexport type InputAxis = \"tx\" | \"ty\" | \"tz\" | \"rx\" | \"ry\" | \"rz\";\n\n/** A single action binding — maps one input axis to a named output */\nexport interface ActionBinding {\n /** Which input axis drives this action */\n source: InputAxis;\n /** Scale multiplier (default: 1) */\n scale?: number;\n /** Invert the value (default: false) */\n invert?: boolean;\n}\n\n/**\n * ActionMap defines how raw 6DOF axes map to named output actions.\n *\n * Client apps declare the actions they support and how device axes\n * feed into them. Users can reassign axes via the settings UI.\n *\n * Default: 6 actions matching the 6 input axes (passthrough).\n */\nexport type ActionMap = Record<string, ActionBinding>;\n\n/** Default passthrough — each axis maps to itself */\nexport const DEFAULT_ACTION_MAP: ActionMap = {\n tx: { source: \"tx\" },\n ty: { source: \"ty\" },\n tz: { source: \"tz\" },\n rx: { source: \"rx\" },\n ry: { source: \"ry\" },\n rz: { source: \"rz\" },\n};\n\n/** Result of applying an ActionMap to spatial data */\nexport type ActionValues = Record<string, number>;\n\n/** Read a raw axis value from SpatialData */\nfunction readAxis(data: SpatialData, axis: InputAxis): number {\n switch (axis) {\n case \"tx\": return data.translation.x;\n case \"ty\": return data.translation.y;\n case \"tz\": return data.translation.z;\n case \"rx\": return data.rotation.x;\n case \"ry\": return data.rotation.y;\n case \"rz\": return data.rotation.z;\n }\n}\n\n/** Apply an ActionMap to SpatialData, producing named action values */\nexport function applyActionMap(data: SpatialData, map: ActionMap): ActionValues {\n const result: ActionValues = {};\n for (const [action, binding] of Object.entries(map)) {\n let value = readAxis(data, binding.source);\n if (binding.invert) value = -value;\n value *= binding.scale ?? 1;\n result[action] = value;\n }\n return result;\n}\n\n/**\n * Convert ActionValues back to SpatialData.\n * Only populates tx/ty/tz/rx/ry/rz keys if they exist as actions.\n */\nexport function actionValuesToSpatialData(values: ActionValues, timestamp: number): SpatialData {\n return {\n translation: {\n x: values.tx ?? 0,\n y: values.ty ?? 0,\n z: values.tz ?? 0,\n },\n rotation: {\n x: values.rx ?? 0,\n y: values.ry ?? 0,\n z: values.rz ?? 0,\n },\n timestamp,\n };\n}\n\n/** Swap two action bindings */\nexport function swapActions(map: ActionMap, actionA: string, actionB: string): ActionMap {\n const result = { ...map };\n const a = result[actionA];\n const b = result[actionB];\n if (a && b) {\n result[actionA] = b;\n result[actionB] = a;\n }\n return result;\n}\n","import type { FlipConfig, SensitivityConfig, AxisMap } from \"./transforms.js\";\nimport { DEFAULT_ACTION_MAP, type ActionMap } from \"./action-map.js\";\n\n/** Per-device transform overrides. Any field left undefined inherits from global defaults. */\nexport interface DeviceConfig {\n sensitivity?: Partial<SensitivityConfig>;\n flip?: Partial<FlipConfig>;\n deadZone?: number;\n dominant?: boolean;\n axisRemap?: Partial<AxisMap>;\n actionMap?: ActionMap;\n lockPosition?: boolean;\n lockRotation?: boolean;\n}\n\nexport interface InputConfig {\n /** Global defaults applied to all devices */\n sensitivity: SensitivityConfig;\n flip: FlipConfig;\n deadZone: number;\n dominant: boolean;\n axisRemap: AxisMap;\n lockPosition: boolean;\n lockRotation: boolean;\n\n /** Action map — maps input axes to named output actions. Default: passthrough. */\n actionMap: ActionMap;\n\n /**\n * Per-device overrides, keyed by device ID (e.g., \"spacemouse-c635\")\n * or device family pattern (e.g., \"spacemouse-*\", \"hid-054c-*\").\n * Values override global defaults for matching devices.\n */\n devices: Record<string, DeviceConfig>;\n}\n\nexport const DEFAULT_CONFIG: InputConfig = {\n sensitivity: { translation: 0.001, rotation: 0.001 },\n flip: { tx: false, ty: true, tz: true, rx: false, ry: true, rz: true },\n deadZone: 0,\n dominant: false,\n axisRemap: { tx: \"x\", ty: \"y\", tz: \"z\", rx: \"x\", ry: \"y\", rz: \"z\" },\n lockPosition: false,\n lockRotation: false,\n actionMap: { ...DEFAULT_ACTION_MAP },\n devices: {},\n};\n\nexport function mergeConfig(base: InputConfig, partial: Partial<InputConfig>): InputConfig {\n const merged = {\n ...base,\n ...partial,\n sensitivity: { ...base.sensitivity, ...partial.sensitivity },\n flip: { ...base.flip, ...partial.flip },\n axisRemap: { ...base.axisRemap, ...partial.axisRemap },\n actionMap: partial.actionMap ? { ...base.actionMap, ...partial.actionMap } : { ...base.actionMap },\n devices: { ...base.devices },\n };\n\n // Merge per-device configs\n if (partial.devices) {\n for (const [key, devCfg] of Object.entries(partial.devices)) {\n merged.devices[key] = mergeDeviceConfig(merged.devices[key], devCfg);\n }\n }\n\n return merged;\n}\n\nexport function mergeDeviceConfig(base: DeviceConfig | undefined, partial: DeviceConfig): DeviceConfig {\n if (!base) return partial;\n return {\n ...base,\n ...partial,\n sensitivity: partial.sensitivity ? { ...base.sensitivity, ...partial.sensitivity } : base.sensitivity,\n flip: partial.flip ? { ...base.flip, ...partial.flip } : base.flip,\n axisRemap: partial.axisRemap ? { ...base.axisRemap, ...partial.axisRemap } : base.axisRemap,\n };\n}\n\n/** Resolve the effective config for a specific device by merging global + device overrides */\nexport function resolveDeviceConfig(config: InputConfig, deviceId: string): InputConfig {\n // Find matching device config: exact match first, then pattern match\n let deviceOverride: DeviceConfig | undefined;\n\n if (config.devices[deviceId]) {\n deviceOverride = config.devices[deviceId];\n } else {\n // Try pattern match (e.g., \"spacemouse-*\" matches \"spacemouse-c635\")\n for (const [pattern, cfg] of Object.entries(config.devices)) {\n if (pattern.endsWith(\"*\") && deviceId.startsWith(pattern.slice(0, -1))) {\n deviceOverride = cfg;\n break;\n }\n }\n }\n\n if (!deviceOverride) return config;\n\n return {\n ...config,\n sensitivity: { ...config.sensitivity, ...deviceOverride.sensitivity },\n flip: { ...config.flip, ...deviceOverride.flip },\n deadZone: deviceOverride.deadZone ?? config.deadZone,\n dominant: deviceOverride.dominant ?? config.dominant,\n axisRemap: { ...config.axisRemap, ...deviceOverride.axisRemap },\n actionMap: deviceOverride.actionMap ? { ...config.actionMap, ...deviceOverride.actionMap } : config.actionMap,\n lockPosition: deviceOverride.lockPosition ?? config.lockPosition,\n lockRotation: deviceOverride.lockRotation ?? config.lockRotation,\n };\n}\n","import type { InputConfig } from \"./config.js\";\n\nexport interface StorageAdapter {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n}\n\nconst STORAGE_KEY = \"satmouse:settings\";\n\nfunction getStorage(storage?: StorageAdapter): StorageAdapter | null {\n if (storage) return storage;\n try {\n return globalThis.localStorage ?? null;\n } catch {\n return null;\n }\n}\n\nexport function saveSettings(config: InputConfig, storage?: StorageAdapter): void {\n const s = getStorage(storage);\n if (!s) return;\n s.setItem(STORAGE_KEY, JSON.stringify(config));\n}\n\nexport function loadSettings(storage?: StorageAdapter): Partial<InputConfig> | null {\n const s = getStorage(storage);\n if (!s) return null;\n const raw = s.getItem(STORAGE_KEY);\n if (!raw) return null;\n try {\n return JSON.parse(raw) as Partial<InputConfig>;\n } catch {\n return null;\n }\n}\n","import type { SpatialData, Vec3 } from \"../core/types.js\";\n\nexport interface FlipConfig {\n tx: boolean;\n ty: boolean;\n tz: boolean;\n rx: boolean;\n ry: boolean;\n rz: boolean;\n}\n\nexport interface SensitivityConfig {\n translation: number;\n rotation: number;\n}\n\n/** Maps each input axis to an output axis. E.g., { tx: \"tz\", tz: \"tx\" } swaps X and Z translation. */\nexport type AxisMap = {\n tx: keyof Vec3;\n ty: keyof Vec3;\n tz: keyof Vec3;\n rx: keyof Vec3;\n ry: keyof Vec3;\n rz: keyof Vec3;\n};\n\nexport const DEFAULT_AXIS_MAP: AxisMap = {\n tx: \"x\", ty: \"y\", tz: \"z\",\n rx: \"x\", ry: \"y\", rz: \"z\",\n};\n\nexport function applyFlip(data: SpatialData, flip: FlipConfig): SpatialData {\n return {\n ...data,\n translation: {\n x: flip.tx ? -data.translation.x : data.translation.x,\n y: flip.ty ? -data.translation.y : data.translation.y,\n z: flip.tz ? -data.translation.z : data.translation.z,\n },\n rotation: {\n x: flip.rx ? -data.rotation.x : data.rotation.x,\n y: flip.ry ? -data.rotation.y : data.rotation.y,\n z: flip.rz ? -data.rotation.z : data.rotation.z,\n },\n };\n}\n\nexport function applySensitivity(data: SpatialData, sens: SensitivityConfig): SpatialData {\n return {\n ...data,\n translation: {\n x: data.translation.x * sens.translation,\n y: data.translation.y * sens.translation,\n z: data.translation.z * sens.translation,\n },\n rotation: {\n x: data.rotation.x * sens.rotation,\n y: data.rotation.y * sens.rotation,\n z: data.rotation.z * sens.rotation,\n },\n };\n}\n\nexport function applyDominant(data: SpatialData): SpatialData {\n const axes = [\n { group: \"t\" as const, key: \"x\" as const, v: Math.abs(data.translation.x) },\n { group: \"t\" as const, key: \"y\" as const, v: Math.abs(data.translation.y) },\n { group: \"t\" as const, key: \"z\" as const, v: Math.abs(data.translation.z) },\n { group: \"r\" as const, key: \"x\" as const, v: Math.abs(data.rotation.x) },\n { group: \"r\" as const, key: \"y\" as const, v: Math.abs(data.rotation.y) },\n { group: \"r\" as const, key: \"z\" as const, v: Math.abs(data.rotation.z) },\n ];\n const max = axes.reduce((a, b) => (b.v > a.v ? b : a));\n\n const t: Vec3 = { x: 0, y: 0, z: 0 };\n const r: Vec3 = { x: 0, y: 0, z: 0 };\n\n if (max.group === \"t\") t[max.key] = data.translation[max.key];\n else r[max.key] = data.rotation[max.key];\n\n return { ...data, translation: t, rotation: r };\n}\n\nexport function applyDeadZone(data: SpatialData, threshold: number): SpatialData {\n const dz = (v: number) => (Math.abs(v) < threshold ? 0 : v);\n return {\n ...data,\n translation: { x: dz(data.translation.x), y: dz(data.translation.y), z: dz(data.translation.z) },\n rotation: { x: dz(data.rotation.x), y: dz(data.rotation.y), z: dz(data.rotation.z) },\n };\n}\n\nexport function applyAxisRemap(data: SpatialData, map: AxisMap): SpatialData {\n return {\n ...data,\n translation: {\n x: 0, y: 0, z: 0,\n [map.tx]: data.translation.x,\n [map.ty]: data.translation.y,\n [map.tz]: data.translation.z,\n } as Vec3,\n rotation: {\n x: 0, y: 0, z: 0,\n [map.rx]: data.rotation.x,\n [map.ry]: data.rotation.y,\n [map.rz]: data.rotation.z,\n } as Vec3,\n };\n}\n","import { TypedEmitter } from \"../core/emitter.js\";\nimport type { SatMouseConnection } from \"../core/connection.js\";\nimport type { SpatialData, ButtonEvent, DeviceInfo, ConnectionState, TransportProtocol } from \"../core/types.js\";\nimport type { InputConfig, DeviceConfig } from \"./config.js\";\nimport { DEFAULT_CONFIG, mergeConfig, resolveDeviceConfig } from \"./config.js\";\nimport { loadSettings, saveSettings, type StorageAdapter } from \"./persistence.js\";\nimport {\n applyFlip,\n applySensitivity,\n applyDominant,\n applyDeadZone,\n applyAxisRemap,\n} from \"./transforms.js\";\nimport { applyActionMap, actionValuesToSpatialData, type ActionValues } from \"./action-map.js\";\n\nexport interface InputManagerEvents {\n /** Processed spatial data (after all transforms + action map) */\n spatialData: (data: SpatialData) => void;\n /** Named action values from the action map */\n actionValues: (values: ActionValues) => void;\n /** Raw spatial data (before transforms) */\n rawSpatialData: (data: SpatialData) => void;\n /** Button event (pass-through from connection) */\n buttonEvent: (data: ButtonEvent) => void;\n /** Connection state changed */\n stateChange: (state: ConnectionState, protocol: TransportProtocol) => void;\n /** Device connected/disconnected */\n deviceStatus: (event: \"connected\" | \"disconnected\", device: DeviceInfo) => void;\n /** Configuration changed */\n configChange: (config: InputConfig) => void;\n}\n\n/** A connected device paired with its resolved configuration */\nexport interface DeviceWithConfig {\n device: DeviceInfo;\n config: DeviceConfig;\n}\n\n/**\n * Unified device service that wraps one or more SatMouseConnections\n * and provides a single processed event stream.\n *\n * Applies a configurable transform pipeline per-device:\n * deadZone → dominant → flip → axisRemap → sensitivity → lock\n *\n * Per-device overrides are resolved from InputConfig.devices using\n * device ID matching (exact or pattern with wildcard \"*\").\n */\nexport class InputManager extends TypedEmitter<InputManagerEvents> {\n private connections: SatMouseConnection[] = [];\n private storage?: StorageAdapter;\n private knownDevices = new Map<string, DeviceInfo>();\n\n private _config: InputConfig;\n\n get config(): InputConfig {\n return this._config;\n }\n\n constructor(config?: Partial<InputConfig>, storage?: StorageAdapter) {\n super();\n this.storage = storage;\n const persisted = loadSettings(storage);\n this._config = mergeConfig(DEFAULT_CONFIG, { ...config, ...persisted });\n }\n\n /** Add a connection to the managed set */\n addConnection(connection: SatMouseConnection): void {\n this.connections.push(connection);\n this.wireConnection(connection);\n }\n\n /** Remove a connection */\n removeConnection(connection: SatMouseConnection): void {\n const idx = this.connections.indexOf(connection);\n if (idx !== -1) this.connections.splice(idx, 1);\n connection.removeAllListeners();\n }\n\n /** Connect all managed connections */\n async connect(): Promise<void> {\n await Promise.all(this.connections.map((c) => c.connect()));\n }\n\n /** Disconnect all managed connections */\n disconnect(): void {\n for (const c of this.connections) c.disconnect();\n }\n\n /** Fetch device info from all connections */\n async fetchDeviceInfo(): Promise<DeviceInfo[]> {\n const results = await Promise.all(this.connections.map((c) => c.fetchDeviceInfo()));\n const devices = results.flat();\n // Track known devices\n for (const d of devices) this.knownDevices.set(d.id, d);\n return devices;\n }\n\n /** Get all known connected devices paired with their resolved config */\n getDevicesWithConfig(): DeviceWithConfig[] {\n return Array.from(this.knownDevices.values()).map((device) => ({\n device,\n config: this.getDeviceConfig(device.id),\n }));\n }\n\n /** Get the resolved per-device config (global defaults + device overrides) */\n getDeviceConfig(deviceId: string): DeviceConfig {\n const resolved = resolveDeviceConfig(this._config, deviceId);\n return {\n sensitivity: resolved.sensitivity,\n flip: resolved.flip,\n deadZone: resolved.deadZone,\n dominant: resolved.dominant,\n axisRemap: resolved.axisRemap,\n actionMap: resolved.actionMap,\n lockPosition: resolved.lockPosition,\n lockRotation: resolved.lockRotation,\n };\n }\n\n /** Update global configuration. Persists by default. */\n updateConfig(partial: Partial<InputConfig>, persist = true): void {\n this._config = mergeConfig(this._config, partial);\n if (persist) saveSettings(this._config, this.storage);\n this.emit(\"configChange\", this._config);\n }\n\n /** Update configuration for a specific device. Persists by default. */\n updateDeviceConfig(deviceId: string, partial: DeviceConfig, persist = true): void {\n const existing = this._config.devices[deviceId] ?? {};\n this._config = mergeConfig(this._config, {\n devices: { [deviceId]: { ...existing, ...partial } },\n });\n if (persist) saveSettings(this._config, this.storage);\n this.emit(\"configChange\", this._config);\n }\n\n /** Register a callback for processed spatial data. Returns unsubscribe function. */\n onSpatialData(callback: (data: SpatialData) => void): () => void {\n this.on(\"spatialData\", callback);\n return () => this.off(\"spatialData\", callback);\n }\n\n /** Register a callback for button events. Returns unsubscribe function. */\n onButtonEvent(callback: (data: ButtonEvent) => void): () => void {\n this.on(\"buttonEvent\", callback);\n return () => this.off(\"buttonEvent\", callback);\n }\n\n /** Register a callback for action values. Returns unsubscribe function. */\n onActionValues(callback: (values: ActionValues) => void): () => void {\n this.on(\"actionValues\", callback);\n return () => this.off(\"actionValues\", callback);\n }\n\n private wireConnection(connection: SatMouseConnection): void {\n connection.on(\"spatialData\", (raw) => {\n this.emit(\"rawSpatialData\", raw);\n const { spatial, actions } = this.processSpatialData(raw);\n if (spatial) this.emit(\"spatialData\", spatial);\n if (actions) this.emit(\"actionValues\", actions);\n });\n\n connection.on(\"buttonEvent\", (event) => this.emit(\"buttonEvent\", event));\n connection.on(\"stateChange\", (state, proto) => this.emit(\"stateChange\", state, proto));\n connection.on(\"deviceStatus\", (event, device) => {\n if (event === \"connected\") this.knownDevices.set(device.id, device);\n else this.knownDevices.delete(device.id);\n this.emit(\"deviceStatus\", event, device);\n });\n }\n\n private processSpatialData(raw: SpatialData): { spatial: SpatialData | null; actions: ActionValues | null } {\n const cfg = this._config;\n let data = raw;\n\n // Transform pipeline\n if (cfg.deadZone > 0) data = applyDeadZone(data, cfg.deadZone);\n if (cfg.dominant) data = applyDominant(data);\n data = applyFlip(data, cfg.flip);\n data = applyAxisRemap(data, cfg.axisRemap);\n data = applySensitivity(data, cfg.sensitivity);\n\n if (cfg.lockPosition) {\n data = { ...data, translation: { x: 0, y: 0, z: 0 } };\n }\n if (cfg.lockRotation) {\n data = { ...data, rotation: { x: 0, y: 0, z: 0 } };\n }\n\n // Apply action map: remap transformed axes to named actions\n const actions = applyActionMap(data, cfg.actionMap);\n\n // Convert action values back to spatial data for consumers that\n // expect the standard tx/ty/tz/rx/ry/rz format\n const spatial = actionValuesToSpatialData(actions, data.timestamp);\n\n return { spatial, actions };\n }\n}\n"]}
|
package/dist/utils/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { V as Vec3, S as SpatialData, e as TypedEmitter, B as ButtonEvent, a as ConnectionState, d as TransportProtocol,
|
|
1
|
+
import { V as Vec3, S as SpatialData, D as DeviceInfo, e as TypedEmitter, B as ButtonEvent, a as ConnectionState, d as TransportProtocol, b as SatMouseConnection } from '../connection-CH2YsPn1.cjs';
|
|
2
2
|
|
|
3
3
|
interface FlipConfig {
|
|
4
4
|
tx: boolean;
|
|
@@ -28,7 +28,53 @@ declare function applyDominant(data: SpatialData): SpatialData;
|
|
|
28
28
|
declare function applyDeadZone(data: SpatialData, threshold: number): SpatialData;
|
|
29
29
|
declare function applyAxisRemap(data: SpatialData, map: AxisMap): SpatialData;
|
|
30
30
|
|
|
31
|
+
/** Input axis identifier */
|
|
32
|
+
type InputAxis = "tx" | "ty" | "tz" | "rx" | "ry" | "rz";
|
|
33
|
+
/** A single action binding — maps one input axis to a named output */
|
|
34
|
+
interface ActionBinding {
|
|
35
|
+
/** Which input axis drives this action */
|
|
36
|
+
source: InputAxis;
|
|
37
|
+
/** Scale multiplier (default: 1) */
|
|
38
|
+
scale?: number;
|
|
39
|
+
/** Invert the value (default: false) */
|
|
40
|
+
invert?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* ActionMap defines how raw 6DOF axes map to named output actions.
|
|
44
|
+
*
|
|
45
|
+
* Client apps declare the actions they support and how device axes
|
|
46
|
+
* feed into them. Users can reassign axes via the settings UI.
|
|
47
|
+
*
|
|
48
|
+
* Default: 6 actions matching the 6 input axes (passthrough).
|
|
49
|
+
*/
|
|
50
|
+
type ActionMap = Record<string, ActionBinding>;
|
|
51
|
+
/** Default passthrough — each axis maps to itself */
|
|
52
|
+
declare const DEFAULT_ACTION_MAP: ActionMap;
|
|
53
|
+
/** Result of applying an ActionMap to spatial data */
|
|
54
|
+
type ActionValues = Record<string, number>;
|
|
55
|
+
/** Apply an ActionMap to SpatialData, producing named action values */
|
|
56
|
+
declare function applyActionMap(data: SpatialData, map: ActionMap): ActionValues;
|
|
57
|
+
/**
|
|
58
|
+
* Convert ActionValues back to SpatialData.
|
|
59
|
+
* Only populates tx/ty/tz/rx/ry/rz keys if they exist as actions.
|
|
60
|
+
*/
|
|
61
|
+
declare function actionValuesToSpatialData(values: ActionValues, timestamp: number): SpatialData;
|
|
62
|
+
/** Swap two action bindings */
|
|
63
|
+
declare function swapActions(map: ActionMap, actionA: string, actionB: string): ActionMap;
|
|
64
|
+
|
|
65
|
+
/** Per-device transform overrides. Any field left undefined inherits from global defaults. */
|
|
66
|
+
interface DeviceConfig {
|
|
67
|
+
sensitivity?: Partial<SensitivityConfig>;
|
|
68
|
+
flip?: Partial<FlipConfig>;
|
|
69
|
+
deadZone?: number;
|
|
70
|
+
dominant?: boolean;
|
|
71
|
+
axisRemap?: Partial<AxisMap>;
|
|
72
|
+
actionMap?: ActionMap;
|
|
73
|
+
lockPosition?: boolean;
|
|
74
|
+
lockRotation?: boolean;
|
|
75
|
+
}
|
|
31
76
|
interface InputConfig {
|
|
77
|
+
/** Global defaults applied to all devices */
|
|
32
78
|
sensitivity: SensitivityConfig;
|
|
33
79
|
flip: FlipConfig;
|
|
34
80
|
deadZone: number;
|
|
@@ -36,9 +82,19 @@ interface InputConfig {
|
|
|
36
82
|
axisRemap: AxisMap;
|
|
37
83
|
lockPosition: boolean;
|
|
38
84
|
lockRotation: boolean;
|
|
85
|
+
/** Action map — maps input axes to named output actions. Default: passthrough. */
|
|
86
|
+
actionMap: ActionMap;
|
|
87
|
+
/**
|
|
88
|
+
* Per-device overrides, keyed by device ID (e.g., "spacemouse-c635")
|
|
89
|
+
* or device family pattern (e.g., "spacemouse-*", "hid-054c-*").
|
|
90
|
+
* Values override global defaults for matching devices.
|
|
91
|
+
*/
|
|
92
|
+
devices: Record<string, DeviceConfig>;
|
|
39
93
|
}
|
|
40
94
|
declare const DEFAULT_CONFIG: InputConfig;
|
|
41
95
|
declare function mergeConfig(base: InputConfig, partial: Partial<InputConfig>): InputConfig;
|
|
96
|
+
/** Resolve the effective config for a specific device by merging global + device overrides */
|
|
97
|
+
declare function resolveDeviceConfig(config: InputConfig, deviceId: string): InputConfig;
|
|
42
98
|
|
|
43
99
|
interface StorageAdapter {
|
|
44
100
|
getItem(key: string): string | null;
|
|
@@ -48,8 +104,10 @@ declare function saveSettings(config: InputConfig, storage?: StorageAdapter): vo
|
|
|
48
104
|
declare function loadSettings(storage?: StorageAdapter): Partial<InputConfig> | null;
|
|
49
105
|
|
|
50
106
|
interface InputManagerEvents {
|
|
51
|
-
/** Processed spatial data (after all transforms) */
|
|
107
|
+
/** Processed spatial data (after all transforms + action map) */
|
|
52
108
|
spatialData: (data: SpatialData) => void;
|
|
109
|
+
/** Named action values from the action map */
|
|
110
|
+
actionValues: (values: ActionValues) => void;
|
|
53
111
|
/** Raw spatial data (before transforms) */
|
|
54
112
|
rawSpatialData: (data: SpatialData) => void;
|
|
55
113
|
/** Button event (pass-through from connection) */
|
|
@@ -61,18 +119,25 @@ interface InputManagerEvents {
|
|
|
61
119
|
/** Configuration changed */
|
|
62
120
|
configChange: (config: InputConfig) => void;
|
|
63
121
|
}
|
|
122
|
+
/** A connected device paired with its resolved configuration */
|
|
123
|
+
interface DeviceWithConfig {
|
|
124
|
+
device: DeviceInfo;
|
|
125
|
+
config: DeviceConfig;
|
|
126
|
+
}
|
|
64
127
|
/**
|
|
65
128
|
* Unified device service that wraps one or more SatMouseConnections
|
|
66
129
|
* and provides a single processed event stream.
|
|
67
130
|
*
|
|
68
|
-
* Applies a configurable transform pipeline
|
|
131
|
+
* Applies a configurable transform pipeline per-device:
|
|
69
132
|
* deadZone → dominant → flip → axisRemap → sensitivity → lock
|
|
70
133
|
*
|
|
71
|
-
*
|
|
134
|
+
* Per-device overrides are resolved from InputConfig.devices using
|
|
135
|
+
* device ID matching (exact or pattern with wildcard "*").
|
|
72
136
|
*/
|
|
73
137
|
declare class InputManager extends TypedEmitter<InputManagerEvents> {
|
|
74
138
|
private connections;
|
|
75
139
|
private storage?;
|
|
140
|
+
private knownDevices;
|
|
76
141
|
private _config;
|
|
77
142
|
get config(): InputConfig;
|
|
78
143
|
constructor(config?: Partial<InputConfig>, storage?: StorageAdapter);
|
|
@@ -86,14 +151,22 @@ declare class InputManager extends TypedEmitter<InputManagerEvents> {
|
|
|
86
151
|
disconnect(): void;
|
|
87
152
|
/** Fetch device info from all connections */
|
|
88
153
|
fetchDeviceInfo(): Promise<DeviceInfo[]>;
|
|
89
|
-
/**
|
|
154
|
+
/** Get all known connected devices paired with their resolved config */
|
|
155
|
+
getDevicesWithConfig(): DeviceWithConfig[];
|
|
156
|
+
/** Get the resolved per-device config (global defaults + device overrides) */
|
|
157
|
+
getDeviceConfig(deviceId: string): DeviceConfig;
|
|
158
|
+
/** Update global configuration. Persists by default. */
|
|
90
159
|
updateConfig(partial: Partial<InputConfig>, persist?: boolean): void;
|
|
160
|
+
/** Update configuration for a specific device. Persists by default. */
|
|
161
|
+
updateDeviceConfig(deviceId: string, partial: DeviceConfig, persist?: boolean): void;
|
|
91
162
|
/** Register a callback for processed spatial data. Returns unsubscribe function. */
|
|
92
163
|
onSpatialData(callback: (data: SpatialData) => void): () => void;
|
|
93
164
|
/** Register a callback for button events. Returns unsubscribe function. */
|
|
94
165
|
onButtonEvent(callback: (data: ButtonEvent) => void): () => void;
|
|
166
|
+
/** Register a callback for action values. Returns unsubscribe function. */
|
|
167
|
+
onActionValues(callback: (values: ActionValues) => void): () => void;
|
|
95
168
|
private wireConnection;
|
|
96
169
|
private processSpatialData;
|
|
97
170
|
}
|
|
98
171
|
|
|
99
|
-
export { type AxisMap, DEFAULT_AXIS_MAP, DEFAULT_CONFIG, type FlipConfig, type InputConfig, InputManager, type InputManagerEvents, type SensitivityConfig, type StorageAdapter, applyAxisRemap, applyDeadZone, applyDominant, applyFlip, applySensitivity, loadSettings, mergeConfig, saveSettings };
|
|
172
|
+
export { type ActionBinding, type ActionMap, type ActionValues, type AxisMap, DEFAULT_ACTION_MAP, DEFAULT_AXIS_MAP, DEFAULT_CONFIG, type DeviceConfig, type DeviceWithConfig, type FlipConfig, type InputAxis, type InputConfig, InputManager, type InputManagerEvents, type SensitivityConfig, type StorageAdapter, actionValuesToSpatialData, applyActionMap, applyAxisRemap, applyDeadZone, applyDominant, applyFlip, applySensitivity, loadSettings, mergeConfig, resolveDeviceConfig, saveSettings, swapActions };
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { V as Vec3, S as SpatialData, e as TypedEmitter, B as ButtonEvent, a as ConnectionState, d as TransportProtocol,
|
|
1
|
+
import { V as Vec3, S as SpatialData, D as DeviceInfo, e as TypedEmitter, B as ButtonEvent, a as ConnectionState, d as TransportProtocol, b as SatMouseConnection } from '../connection-CH2YsPn1.js';
|
|
2
2
|
|
|
3
3
|
interface FlipConfig {
|
|
4
4
|
tx: boolean;
|
|
@@ -28,7 +28,53 @@ declare function applyDominant(data: SpatialData): SpatialData;
|
|
|
28
28
|
declare function applyDeadZone(data: SpatialData, threshold: number): SpatialData;
|
|
29
29
|
declare function applyAxisRemap(data: SpatialData, map: AxisMap): SpatialData;
|
|
30
30
|
|
|
31
|
+
/** Input axis identifier */
|
|
32
|
+
type InputAxis = "tx" | "ty" | "tz" | "rx" | "ry" | "rz";
|
|
33
|
+
/** A single action binding — maps one input axis to a named output */
|
|
34
|
+
interface ActionBinding {
|
|
35
|
+
/** Which input axis drives this action */
|
|
36
|
+
source: InputAxis;
|
|
37
|
+
/** Scale multiplier (default: 1) */
|
|
38
|
+
scale?: number;
|
|
39
|
+
/** Invert the value (default: false) */
|
|
40
|
+
invert?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* ActionMap defines how raw 6DOF axes map to named output actions.
|
|
44
|
+
*
|
|
45
|
+
* Client apps declare the actions they support and how device axes
|
|
46
|
+
* feed into them. Users can reassign axes via the settings UI.
|
|
47
|
+
*
|
|
48
|
+
* Default: 6 actions matching the 6 input axes (passthrough).
|
|
49
|
+
*/
|
|
50
|
+
type ActionMap = Record<string, ActionBinding>;
|
|
51
|
+
/** Default passthrough — each axis maps to itself */
|
|
52
|
+
declare const DEFAULT_ACTION_MAP: ActionMap;
|
|
53
|
+
/** Result of applying an ActionMap to spatial data */
|
|
54
|
+
type ActionValues = Record<string, number>;
|
|
55
|
+
/** Apply an ActionMap to SpatialData, producing named action values */
|
|
56
|
+
declare function applyActionMap(data: SpatialData, map: ActionMap): ActionValues;
|
|
57
|
+
/**
|
|
58
|
+
* Convert ActionValues back to SpatialData.
|
|
59
|
+
* Only populates tx/ty/tz/rx/ry/rz keys if they exist as actions.
|
|
60
|
+
*/
|
|
61
|
+
declare function actionValuesToSpatialData(values: ActionValues, timestamp: number): SpatialData;
|
|
62
|
+
/** Swap two action bindings */
|
|
63
|
+
declare function swapActions(map: ActionMap, actionA: string, actionB: string): ActionMap;
|
|
64
|
+
|
|
65
|
+
/** Per-device transform overrides. Any field left undefined inherits from global defaults. */
|
|
66
|
+
interface DeviceConfig {
|
|
67
|
+
sensitivity?: Partial<SensitivityConfig>;
|
|
68
|
+
flip?: Partial<FlipConfig>;
|
|
69
|
+
deadZone?: number;
|
|
70
|
+
dominant?: boolean;
|
|
71
|
+
axisRemap?: Partial<AxisMap>;
|
|
72
|
+
actionMap?: ActionMap;
|
|
73
|
+
lockPosition?: boolean;
|
|
74
|
+
lockRotation?: boolean;
|
|
75
|
+
}
|
|
31
76
|
interface InputConfig {
|
|
77
|
+
/** Global defaults applied to all devices */
|
|
32
78
|
sensitivity: SensitivityConfig;
|
|
33
79
|
flip: FlipConfig;
|
|
34
80
|
deadZone: number;
|
|
@@ -36,9 +82,19 @@ interface InputConfig {
|
|
|
36
82
|
axisRemap: AxisMap;
|
|
37
83
|
lockPosition: boolean;
|
|
38
84
|
lockRotation: boolean;
|
|
85
|
+
/** Action map — maps input axes to named output actions. Default: passthrough. */
|
|
86
|
+
actionMap: ActionMap;
|
|
87
|
+
/**
|
|
88
|
+
* Per-device overrides, keyed by device ID (e.g., "spacemouse-c635")
|
|
89
|
+
* or device family pattern (e.g., "spacemouse-*", "hid-054c-*").
|
|
90
|
+
* Values override global defaults for matching devices.
|
|
91
|
+
*/
|
|
92
|
+
devices: Record<string, DeviceConfig>;
|
|
39
93
|
}
|
|
40
94
|
declare const DEFAULT_CONFIG: InputConfig;
|
|
41
95
|
declare function mergeConfig(base: InputConfig, partial: Partial<InputConfig>): InputConfig;
|
|
96
|
+
/** Resolve the effective config for a specific device by merging global + device overrides */
|
|
97
|
+
declare function resolveDeviceConfig(config: InputConfig, deviceId: string): InputConfig;
|
|
42
98
|
|
|
43
99
|
interface StorageAdapter {
|
|
44
100
|
getItem(key: string): string | null;
|
|
@@ -48,8 +104,10 @@ declare function saveSettings(config: InputConfig, storage?: StorageAdapter): vo
|
|
|
48
104
|
declare function loadSettings(storage?: StorageAdapter): Partial<InputConfig> | null;
|
|
49
105
|
|
|
50
106
|
interface InputManagerEvents {
|
|
51
|
-
/** Processed spatial data (after all transforms) */
|
|
107
|
+
/** Processed spatial data (after all transforms + action map) */
|
|
52
108
|
spatialData: (data: SpatialData) => void;
|
|
109
|
+
/** Named action values from the action map */
|
|
110
|
+
actionValues: (values: ActionValues) => void;
|
|
53
111
|
/** Raw spatial data (before transforms) */
|
|
54
112
|
rawSpatialData: (data: SpatialData) => void;
|
|
55
113
|
/** Button event (pass-through from connection) */
|
|
@@ -61,18 +119,25 @@ interface InputManagerEvents {
|
|
|
61
119
|
/** Configuration changed */
|
|
62
120
|
configChange: (config: InputConfig) => void;
|
|
63
121
|
}
|
|
122
|
+
/** A connected device paired with its resolved configuration */
|
|
123
|
+
interface DeviceWithConfig {
|
|
124
|
+
device: DeviceInfo;
|
|
125
|
+
config: DeviceConfig;
|
|
126
|
+
}
|
|
64
127
|
/**
|
|
65
128
|
* Unified device service that wraps one or more SatMouseConnections
|
|
66
129
|
* and provides a single processed event stream.
|
|
67
130
|
*
|
|
68
|
-
* Applies a configurable transform pipeline
|
|
131
|
+
* Applies a configurable transform pipeline per-device:
|
|
69
132
|
* deadZone → dominant → flip → axisRemap → sensitivity → lock
|
|
70
133
|
*
|
|
71
|
-
*
|
|
134
|
+
* Per-device overrides are resolved from InputConfig.devices using
|
|
135
|
+
* device ID matching (exact or pattern with wildcard "*").
|
|
72
136
|
*/
|
|
73
137
|
declare class InputManager extends TypedEmitter<InputManagerEvents> {
|
|
74
138
|
private connections;
|
|
75
139
|
private storage?;
|
|
140
|
+
private knownDevices;
|
|
76
141
|
private _config;
|
|
77
142
|
get config(): InputConfig;
|
|
78
143
|
constructor(config?: Partial<InputConfig>, storage?: StorageAdapter);
|
|
@@ -86,14 +151,22 @@ declare class InputManager extends TypedEmitter<InputManagerEvents> {
|
|
|
86
151
|
disconnect(): void;
|
|
87
152
|
/** Fetch device info from all connections */
|
|
88
153
|
fetchDeviceInfo(): Promise<DeviceInfo[]>;
|
|
89
|
-
/**
|
|
154
|
+
/** Get all known connected devices paired with their resolved config */
|
|
155
|
+
getDevicesWithConfig(): DeviceWithConfig[];
|
|
156
|
+
/** Get the resolved per-device config (global defaults + device overrides) */
|
|
157
|
+
getDeviceConfig(deviceId: string): DeviceConfig;
|
|
158
|
+
/** Update global configuration. Persists by default. */
|
|
90
159
|
updateConfig(partial: Partial<InputConfig>, persist?: boolean): void;
|
|
160
|
+
/** Update configuration for a specific device. Persists by default. */
|
|
161
|
+
updateDeviceConfig(deviceId: string, partial: DeviceConfig, persist?: boolean): void;
|
|
91
162
|
/** Register a callback for processed spatial data. Returns unsubscribe function. */
|
|
92
163
|
onSpatialData(callback: (data: SpatialData) => void): () => void;
|
|
93
164
|
/** Register a callback for button events. Returns unsubscribe function. */
|
|
94
165
|
onButtonEvent(callback: (data: ButtonEvent) => void): () => void;
|
|
166
|
+
/** Register a callback for action values. Returns unsubscribe function. */
|
|
167
|
+
onActionValues(callback: (values: ActionValues) => void): () => void;
|
|
95
168
|
private wireConnection;
|
|
96
169
|
private processSpatialData;
|
|
97
170
|
}
|
|
98
171
|
|
|
99
|
-
export { type AxisMap, DEFAULT_AXIS_MAP, DEFAULT_CONFIG, type FlipConfig, type InputConfig, InputManager, type InputManagerEvents, type SensitivityConfig, type StorageAdapter, applyAxisRemap, applyDeadZone, applyDominant, applyFlip, applySensitivity, loadSettings, mergeConfig, saveSettings };
|
|
172
|
+
export { type ActionBinding, type ActionMap, type ActionValues, type AxisMap, DEFAULT_ACTION_MAP, DEFAULT_AXIS_MAP, DEFAULT_CONFIG, type DeviceConfig, type DeviceWithConfig, type FlipConfig, type InputAxis, type InputConfig, InputManager, type InputManagerEvents, type SensitivityConfig, type StorageAdapter, actionValuesToSpatialData, applyActionMap, applyAxisRemap, applyDeadZone, applyDominant, applyFlip, applySensitivity, loadSettings, mergeConfig, resolveDeviceConfig, saveSettings, swapActions };
|