@kelnishi/satmouse-client 0.1.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.
@@ -0,0 +1,99 @@
1
+ import { V as Vec3, S as SpatialData, e as TypedEmitter, B as ButtonEvent, a as ConnectionState, d as TransportProtocol, D as DeviceInfo, b as SatMouseConnection } from '../connection-CH2YsPn1.js';
2
+
3
+ interface FlipConfig {
4
+ tx: boolean;
5
+ ty: boolean;
6
+ tz: boolean;
7
+ rx: boolean;
8
+ ry: boolean;
9
+ rz: boolean;
10
+ }
11
+ interface SensitivityConfig {
12
+ translation: number;
13
+ rotation: number;
14
+ }
15
+ /** Maps each input axis to an output axis. E.g., { tx: "tz", tz: "tx" } swaps X and Z translation. */
16
+ type AxisMap = {
17
+ tx: keyof Vec3;
18
+ ty: keyof Vec3;
19
+ tz: keyof Vec3;
20
+ rx: keyof Vec3;
21
+ ry: keyof Vec3;
22
+ rz: keyof Vec3;
23
+ };
24
+ declare const DEFAULT_AXIS_MAP: AxisMap;
25
+ declare function applyFlip(data: SpatialData, flip: FlipConfig): SpatialData;
26
+ declare function applySensitivity(data: SpatialData, sens: SensitivityConfig): SpatialData;
27
+ declare function applyDominant(data: SpatialData): SpatialData;
28
+ declare function applyDeadZone(data: SpatialData, threshold: number): SpatialData;
29
+ declare function applyAxisRemap(data: SpatialData, map: AxisMap): SpatialData;
30
+
31
+ interface InputConfig {
32
+ sensitivity: SensitivityConfig;
33
+ flip: FlipConfig;
34
+ deadZone: number;
35
+ dominant: boolean;
36
+ axisRemap: AxisMap;
37
+ lockPosition: boolean;
38
+ lockRotation: boolean;
39
+ }
40
+ declare const DEFAULT_CONFIG: InputConfig;
41
+ declare function mergeConfig(base: InputConfig, partial: Partial<InputConfig>): InputConfig;
42
+
43
+ interface StorageAdapter {
44
+ getItem(key: string): string | null;
45
+ setItem(key: string, value: string): void;
46
+ }
47
+ declare function saveSettings(config: InputConfig, storage?: StorageAdapter): void;
48
+ declare function loadSettings(storage?: StorageAdapter): Partial<InputConfig> | null;
49
+
50
+ interface InputManagerEvents {
51
+ /** Processed spatial data (after all transforms) */
52
+ spatialData: (data: SpatialData) => void;
53
+ /** Raw spatial data (before transforms) */
54
+ rawSpatialData: (data: SpatialData) => void;
55
+ /** Button event (pass-through from connection) */
56
+ buttonEvent: (data: ButtonEvent) => void;
57
+ /** Connection state changed */
58
+ stateChange: (state: ConnectionState, protocol: TransportProtocol) => void;
59
+ /** Device connected/disconnected */
60
+ deviceStatus: (event: "connected" | "disconnected", device: DeviceInfo) => void;
61
+ /** Configuration changed */
62
+ configChange: (config: InputConfig) => void;
63
+ }
64
+ /**
65
+ * Unified device service that wraps one or more SatMouseConnections
66
+ * and provides a single processed event stream.
67
+ *
68
+ * Applies a configurable transform pipeline to spatial data:
69
+ * deadZone → dominant → flip → axisRemap → sensitivity → lock
70
+ *
71
+ * Persists settings to storage (localStorage by default).
72
+ */
73
+ declare class InputManager extends TypedEmitter<InputManagerEvents> {
74
+ private connections;
75
+ private storage?;
76
+ private _config;
77
+ get config(): InputConfig;
78
+ constructor(config?: Partial<InputConfig>, storage?: StorageAdapter);
79
+ /** Add a connection to the managed set */
80
+ addConnection(connection: SatMouseConnection): void;
81
+ /** Remove a connection */
82
+ removeConnection(connection: SatMouseConnection): void;
83
+ /** Connect all managed connections */
84
+ connect(): Promise<void>;
85
+ /** Disconnect all managed connections */
86
+ disconnect(): void;
87
+ /** Fetch device info from all connections */
88
+ fetchDeviceInfo(): Promise<DeviceInfo[]>;
89
+ /** Update configuration. Persists by default. */
90
+ updateConfig(partial: Partial<InputConfig>, persist?: boolean): void;
91
+ /** Register a callback for processed spatial data. Returns unsubscribe function. */
92
+ onSpatialData(callback: (data: SpatialData) => void): () => void;
93
+ /** Register a callback for button events. Returns unsubscribe function. */
94
+ onButtonEvent(callback: (data: ButtonEvent) => void): () => void;
95
+ private wireConnection;
96
+ private processSpatialData;
97
+ }
98
+
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 };
@@ -0,0 +1,219 @@
1
+ import { TypedEmitter } from '../chunk-X6NIARXW.js';
2
+
3
+ // src/utils/config.ts
4
+ var DEFAULT_CONFIG = {
5
+ sensitivity: { translation: 1e-3, rotation: 1e-3 },
6
+ flip: { tx: false, ty: true, tz: true, rx: false, ry: true, rz: true },
7
+ deadZone: 0,
8
+ dominant: false,
9
+ axisRemap: { tx: "x", ty: "y", tz: "z", rx: "x", ry: "y", rz: "z" },
10
+ lockPosition: false,
11
+ lockRotation: false
12
+ };
13
+ function mergeConfig(base, partial) {
14
+ return {
15
+ ...base,
16
+ ...partial,
17
+ sensitivity: { ...base.sensitivity, ...partial.sensitivity },
18
+ flip: { ...base.flip, ...partial.flip },
19
+ axisRemap: { ...base.axisRemap, ...partial.axisRemap }
20
+ };
21
+ }
22
+
23
+ // src/utils/persistence.ts
24
+ var STORAGE_KEY = "satmouse:settings";
25
+ function getStorage(storage) {
26
+ if (storage) return storage;
27
+ try {
28
+ return globalThis.localStorage ?? null;
29
+ } catch {
30
+ return null;
31
+ }
32
+ }
33
+ function saveSettings(config, storage) {
34
+ const s = getStorage(storage);
35
+ if (!s) return;
36
+ s.setItem(STORAGE_KEY, JSON.stringify(config));
37
+ }
38
+ function loadSettings(storage) {
39
+ const s = getStorage(storage);
40
+ if (!s) return null;
41
+ const raw = s.getItem(STORAGE_KEY);
42
+ if (!raw) return null;
43
+ try {
44
+ return JSON.parse(raw);
45
+ } catch {
46
+ return null;
47
+ }
48
+ }
49
+
50
+ // src/utils/transforms.ts
51
+ var DEFAULT_AXIS_MAP = {
52
+ tx: "x",
53
+ ty: "y",
54
+ tz: "z",
55
+ rx: "x",
56
+ ry: "y",
57
+ rz: "z"
58
+ };
59
+ function applyFlip(data, flip) {
60
+ return {
61
+ ...data,
62
+ translation: {
63
+ x: flip.tx ? -data.translation.x : data.translation.x,
64
+ y: flip.ty ? -data.translation.y : data.translation.y,
65
+ z: flip.tz ? -data.translation.z : data.translation.z
66
+ },
67
+ rotation: {
68
+ x: flip.rx ? -data.rotation.x : data.rotation.x,
69
+ y: flip.ry ? -data.rotation.y : data.rotation.y,
70
+ z: flip.rz ? -data.rotation.z : data.rotation.z
71
+ }
72
+ };
73
+ }
74
+ function applySensitivity(data, sens) {
75
+ return {
76
+ ...data,
77
+ translation: {
78
+ x: data.translation.x * sens.translation,
79
+ y: data.translation.y * sens.translation,
80
+ z: data.translation.z * sens.translation
81
+ },
82
+ rotation: {
83
+ x: data.rotation.x * sens.rotation,
84
+ y: data.rotation.y * sens.rotation,
85
+ z: data.rotation.z * sens.rotation
86
+ }
87
+ };
88
+ }
89
+ function applyDominant(data) {
90
+ const axes = [
91
+ { group: "t", key: "x", v: Math.abs(data.translation.x) },
92
+ { group: "t", key: "y", v: Math.abs(data.translation.y) },
93
+ { group: "t", key: "z", v: Math.abs(data.translation.z) },
94
+ { group: "r", key: "x", v: Math.abs(data.rotation.x) },
95
+ { group: "r", key: "y", v: Math.abs(data.rotation.y) },
96
+ { group: "r", key: "z", v: Math.abs(data.rotation.z) }
97
+ ];
98
+ const max = axes.reduce((a, b) => b.v > a.v ? b : a);
99
+ const t = { x: 0, y: 0, z: 0 };
100
+ const r = { x: 0, y: 0, z: 0 };
101
+ if (max.group === "t") t[max.key] = data.translation[max.key];
102
+ else r[max.key] = data.rotation[max.key];
103
+ return { ...data, translation: t, rotation: r };
104
+ }
105
+ function applyDeadZone(data, threshold) {
106
+ const dz = (v) => Math.abs(v) < threshold ? 0 : v;
107
+ return {
108
+ ...data,
109
+ translation: { x: dz(data.translation.x), y: dz(data.translation.y), z: dz(data.translation.z) },
110
+ rotation: { x: dz(data.rotation.x), y: dz(data.rotation.y), z: dz(data.rotation.z) }
111
+ };
112
+ }
113
+ function applyAxisRemap(data, map) {
114
+ return {
115
+ ...data,
116
+ translation: {
117
+ x: 0,
118
+ y: 0,
119
+ z: 0,
120
+ [map.tx]: data.translation.x,
121
+ [map.ty]: data.translation.y,
122
+ [map.tz]: data.translation.z
123
+ },
124
+ rotation: {
125
+ x: 0,
126
+ y: 0,
127
+ z: 0,
128
+ [map.rx]: data.rotation.x,
129
+ [map.ry]: data.rotation.y,
130
+ [map.rz]: data.rotation.z
131
+ }
132
+ };
133
+ }
134
+
135
+ // src/utils/input-manager.ts
136
+ var InputManager = class extends TypedEmitter {
137
+ connections = [];
138
+ storage;
139
+ _config;
140
+ get config() {
141
+ return this._config;
142
+ }
143
+ constructor(config, storage) {
144
+ super();
145
+ this.storage = storage;
146
+ const persisted = loadSettings(storage);
147
+ this._config = mergeConfig(DEFAULT_CONFIG, { ...config, ...persisted });
148
+ }
149
+ /** Add a connection to the managed set */
150
+ addConnection(connection) {
151
+ this.connections.push(connection);
152
+ this.wireConnection(connection);
153
+ }
154
+ /** Remove a connection */
155
+ removeConnection(connection) {
156
+ const idx = this.connections.indexOf(connection);
157
+ if (idx !== -1) this.connections.splice(idx, 1);
158
+ connection.removeAllListeners();
159
+ }
160
+ /** Connect all managed connections */
161
+ async connect() {
162
+ await Promise.all(this.connections.map((c) => c.connect()));
163
+ }
164
+ /** Disconnect all managed connections */
165
+ disconnect() {
166
+ for (const c of this.connections) c.disconnect();
167
+ }
168
+ /** Fetch device info from all connections */
169
+ async fetchDeviceInfo() {
170
+ const results = await Promise.all(this.connections.map((c) => c.fetchDeviceInfo()));
171
+ return results.flat();
172
+ }
173
+ /** Update configuration. Persists by default. */
174
+ updateConfig(partial, persist = true) {
175
+ this._config = mergeConfig(this._config, partial);
176
+ if (persist) saveSettings(this._config, this.storage);
177
+ this.emit("configChange", this._config);
178
+ }
179
+ /** Register a callback for processed spatial data. Returns unsubscribe function. */
180
+ onSpatialData(callback) {
181
+ this.on("spatialData", callback);
182
+ return () => this.off("spatialData", callback);
183
+ }
184
+ /** Register a callback for button events. Returns unsubscribe function. */
185
+ onButtonEvent(callback) {
186
+ this.on("buttonEvent", callback);
187
+ return () => this.off("buttonEvent", callback);
188
+ }
189
+ wireConnection(connection) {
190
+ connection.on("spatialData", (raw) => {
191
+ this.emit("rawSpatialData", raw);
192
+ const processed = this.processSpatialData(raw);
193
+ if (processed) this.emit("spatialData", processed);
194
+ });
195
+ connection.on("buttonEvent", (event) => this.emit("buttonEvent", event));
196
+ connection.on("stateChange", (state, proto) => this.emit("stateChange", state, proto));
197
+ connection.on("deviceStatus", (event, device) => this.emit("deviceStatus", event, device));
198
+ }
199
+ processSpatialData(raw) {
200
+ const cfg = this._config;
201
+ let data = raw;
202
+ if (cfg.deadZone > 0) data = applyDeadZone(data, cfg.deadZone);
203
+ if (cfg.dominant) data = applyDominant(data);
204
+ data = applyFlip(data, cfg.flip);
205
+ data = applyAxisRemap(data, cfg.axisRemap);
206
+ data = applySensitivity(data, cfg.sensitivity);
207
+ if (cfg.lockPosition) {
208
+ data = { ...data, translation: { x: 0, y: 0, z: 0 } };
209
+ }
210
+ if (cfg.lockRotation) {
211
+ data = { ...data, rotation: { x: 0, y: 0, z: 0 } };
212
+ }
213
+ return data;
214
+ }
215
+ };
216
+
217
+ export { DEFAULT_AXIS_MAP, DEFAULT_CONFIG, InputManager, applyAxisRemap, applyDeadZone, applyDominant, applyFlip, applySensitivity, loadSettings, mergeConfig, saveSettings };
218
+ //# sourceMappingURL=index.js.map
219
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/config.ts","../../src/utils/persistence.ts","../../src/utils/transforms.ts","../../src/utils/input-manager.ts"],"names":[],"mappings":"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","file":"index.js","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"]}
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@kelnishi/satmouse-client",
3
+ "version": "0.1.0",
4
+ "description": "Client SDK for SatMouse 6DOF spatial input bridge",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/core/index.d.ts",
10
+ "import": "./dist/core/index.mjs",
11
+ "require": "./dist/core/index.cjs"
12
+ },
13
+ "./core": {
14
+ "types": "./dist/core/index.d.ts",
15
+ "import": "./dist/core/index.mjs",
16
+ "require": "./dist/core/index.cjs"
17
+ },
18
+ "./utils": {
19
+ "types": "./dist/utils/index.d.ts",
20
+ "import": "./dist/utils/index.mjs",
21
+ "require": "./dist/utils/index.cjs"
22
+ },
23
+ "./react": {
24
+ "types": "./dist/react/index.d.ts",
25
+ "import": "./dist/react/index.mjs",
26
+ "require": "./dist/react/index.cjs"
27
+ },
28
+ "./react/styles.css": "./dist/styles.css"
29
+ },
30
+ "typesVersions": {
31
+ "*": {
32
+ "core": ["dist/core/index.d.ts"],
33
+ "utils": ["dist/utils/index.d.ts"],
34
+ "react": ["dist/react/index.d.ts"]
35
+ }
36
+ },
37
+ "files": ["dist"],
38
+ "scripts": {
39
+ "build": "tsup",
40
+ "dev": "tsup --watch",
41
+ "typecheck": "tsc --noEmit"
42
+ },
43
+ "peerDependencies": {
44
+ "react": "^18.0.0 || ^19.0.0"
45
+ },
46
+ "peerDependenciesMeta": {
47
+ "react": { "optional": true }
48
+ },
49
+ "devDependencies": {
50
+ "@types/react": "^19.0.0",
51
+ "react": "^19.0.0",
52
+ "tsup": "^8.0.0",
53
+ "typescript": "^5.7.0"
54
+ },
55
+ "license": "MIT",
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "https://github.com/kelnishi/SatMouse.git",
59
+ "directory": "packages/client"
60
+ },
61
+ "publishConfig": {
62
+ "access": "public"
63
+ }
64
+ }