@kelnishi/satmouse-client 0.10.1 → 0.10.2

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,253 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+ import { ReactNode } from 'react';
4
+
5
+ /** Minimal typed event emitter — no node:events dependency for browser compatibility */
6
+ declare class TypedEmitter<Events> {
7
+ private listeners;
8
+ on<K extends string & keyof Events>(event: K, listener: Events[K] & Function): this;
9
+ off<K extends string & keyof Events>(event: K, listener: Events[K] & Function): this;
10
+ protected emit<K extends string & keyof Events>(event: K, ...args: Events[K] extends (...a: infer A) => void ? A : never): void;
11
+ removeAllListeners(): void;
12
+ }
13
+
14
+ interface Vec3 {
15
+ x: number;
16
+ y: number;
17
+ z: number;
18
+ }
19
+ /** 6DOF spatial input frame — matches spatial-data.schema.json */
20
+ interface SpatialData {
21
+ translation: Vec3;
22
+ rotation: Vec3;
23
+ timestamp: number;
24
+ /** Source device ID (e.g., "cnx-c635", "hid-054c-5c4") */
25
+ deviceId?: string;
26
+ }
27
+ /** Button press/release event — matches button-event.schema.json */
28
+ interface ButtonEvent {
29
+ button: number;
30
+ pressed: boolean;
31
+ timestamp: number;
32
+ }
33
+ /** Device metadata from the bridge */
34
+ interface DeviceInfo$1 {
35
+ id: string;
36
+ name: string;
37
+ model?: string;
38
+ vendor?: string;
39
+ vendorId?: number;
40
+ productId?: number;
41
+ connectionType?: "usb" | "wireless" | "bluetooth" | "unknown";
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;
49
+ }
50
+ type ConnectionState = "disconnected" | "connecting" | "connected" | "failed";
51
+ type TransportProtocol = "webtransport" | "websocket" | "none";
52
+ interface SatMouseEvents {
53
+ spatialData: (data: SpatialData) => void;
54
+ buttonEvent: (data: ButtonEvent) => void;
55
+ stateChange: (state: ConnectionState, protocol: TransportProtocol) => void;
56
+ deviceStatus: (event: "connected" | "disconnected", device: DeviceInfo$1) => void;
57
+ error: (error: Error) => void;
58
+ }
59
+ interface ConnectOptions {
60
+ /**
61
+ * SatMouse URI: satmouse://connect?host=<ip>&wsPort=<port>&wtPort=<port>
62
+ * When provided, host/ports are extracted and used for discovery + transport.
63
+ * All params are optional — defaults to localhost:4444/4443.
64
+ */
65
+ uri?: string;
66
+ /** URL to td.json. Defaults to /td.json relative to window.location */
67
+ tdUrl?: string;
68
+ /** Direct WebSocket URL (skips discovery) */
69
+ wsUrl?: string;
70
+ /** Direct WebTransport URL (skips discovery) */
71
+ wtUrl?: string;
72
+ /** Certificate hash for self-signed WebTransport certs (base64) */
73
+ certHash?: string;
74
+ /** Preferred transport order. Default: ["webtransport", "websocket"] */
75
+ transports?: TransportProtocol[];
76
+ /** Auto-reconnect delay in ms. 0 to disable. Default: 2000 */
77
+ reconnectDelay?: number;
78
+ /** Max reconnect attempts before giving up. Default: 3 */
79
+ maxRetries?: number;
80
+ /** WebSocket subprotocol. Default: "satmouse-json" */
81
+ wsSubprotocol?: "satmouse-json" | "satmouse-binary";
82
+ }
83
+
84
+ /**
85
+ * Core connection to a SatMouse bridge.
86
+ *
87
+ * Handles discovery (via WoT Thing Description), transport negotiation
88
+ * (WebTransport with WebSocket fallback), event dispatch, and auto-reconnect.
89
+ */
90
+ declare class SatMouseConnection extends TypedEmitter<SatMouseEvents> {
91
+ private options;
92
+ private transport;
93
+ private reconnectTimer;
94
+ private intentionalClose;
95
+ private deviceInfoUrl;
96
+ private retryCount;
97
+ private _state;
98
+ private _protocol;
99
+ get state(): ConnectionState;
100
+ get protocol(): TransportProtocol;
101
+ constructor(options?: ConnectOptions);
102
+ connect(): Promise<void>;
103
+ /** Reset retry count and reconnect. Use after "failed" state. */
104
+ retry(): void;
105
+ disconnect(): void;
106
+ fetchDeviceInfo(): Promise<DeviceInfo$1[]>;
107
+ private tryTransport;
108
+ private setState;
109
+ private scheduleReconnect;
110
+ private clearReconnect;
111
+ }
112
+
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 {
117
+ source: InputAxis;
118
+ target: InputAxis;
119
+ /** Negate the value (default: false) */
120
+ flip?: boolean;
121
+ }
122
+
123
+ /** Per-device configuration */
124
+ interface DeviceConfig {
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. */
130
+ deadZone?: number;
131
+ /** Only pass the strongest axis, zero all others */
132
+ dominant?: boolean;
133
+ }
134
+ /** Global configuration */
135
+ interface InputConfig {
136
+ /** Default axis routes (used when device has no override) */
137
+ routes: AxisRoute[];
138
+ /** Default scale */
139
+ scale: number;
140
+ /** Dead zone threshold */
141
+ deadZone: number;
142
+ /** Dominant axis mode */
143
+ dominant: boolean;
144
+ /** Lock translation to zero */
145
+ lockPosition: boolean;
146
+ /** Lock rotation to zero */
147
+ lockRotation: boolean;
148
+ /** Per-device overrides keyed by device ID or pattern (e.g., "cnx-*") */
149
+ devices: Record<string, DeviceConfig>;
150
+ }
151
+
152
+ interface StorageAdapter {
153
+ getItem(key: string): string | null;
154
+ setItem(key: string, value: string): void;
155
+ }
156
+
157
+ interface InputManagerEvents {
158
+ spatialData: (data: SpatialData) => void;
159
+ rawSpatialData: (data: SpatialData) => void;
160
+ buttonEvent: (data: ButtonEvent) => void;
161
+ stateChange: (state: ConnectionState, protocol: TransportProtocol) => void;
162
+ deviceStatus: (event: "connected" | "disconnected", device: DeviceInfo$1) => void;
163
+ configChange: (config: InputConfig) => void;
164
+ }
165
+ interface DeviceWithConfig {
166
+ device: DeviceInfo$1;
167
+ config: DeviceConfig;
168
+ }
169
+ declare class InputManager extends TypedEmitter<InputManagerEvents> {
170
+ private connections;
171
+ private storage?;
172
+ private knownDevices;
173
+ private deviceAccumulators;
174
+ private accDirty;
175
+ private flushTimer;
176
+ private _config;
177
+ get config(): InputConfig;
178
+ constructor(config?: Partial<InputConfig>, storage?: StorageAdapter);
179
+ addConnection(connection: SatMouseConnection): void;
180
+ removeConnection(connection: SatMouseConnection): void;
181
+ connect(): Promise<void>;
182
+ disconnect(): void;
183
+ fetchDeviceInfo(): Promise<DeviceInfo$1[]>;
184
+ getDevicesWithConfig(): DeviceWithConfig[];
185
+ getDeviceConfig(deviceId: string): DeviceConfig;
186
+ updateConfig(partial: Partial<InputConfig>, persist?: boolean): void;
187
+ updateDeviceConfig(deviceId: string, partial: DeviceConfig, persist?: boolean): void;
188
+ resetDeviceConfig(deviceId: string, persist?: boolean): void;
189
+ resetAllConfig(): void;
190
+ onSpatialData(callback: (data: SpatialData) => void): () => void;
191
+ onButtonEvent(callback: (data: ButtonEvent) => void): () => void;
192
+ private wireConnection;
193
+ private flushAccumulator;
194
+ /** Per-device: deadZone → dominant → routes (flip + scale + remap in one pass) */
195
+ private processPerDevice;
196
+ /** Get the effective routes for a device: device config override > device axes metadata > global default */
197
+ private resolveRoutes;
198
+ }
199
+
200
+ interface SatMouseContextValue {
201
+ manager: InputManager;
202
+ state: ConnectionState;
203
+ protocol: TransportProtocol;
204
+ config: InputConfig;
205
+ updateConfig: (partial: Partial<InputConfig>) => void;
206
+ }
207
+ declare const SatMouseContext: react.Context<SatMouseContextValue | null>;
208
+ interface SatMouseProviderProps {
209
+ connectOptions?: ConnectOptions;
210
+ config?: Partial<InputConfig>;
211
+ autoConnect?: boolean;
212
+ children: ReactNode;
213
+ }
214
+ declare function SatMouseProvider({ connectOptions, config: configOverrides, autoConnect, children, }: SatMouseProviderProps): react_jsx_runtime.JSX.Element;
215
+
216
+ /** Access the SatMouse context. Throws if used outside SatMouseProvider. */
217
+ declare function useSatMouse(): SatMouseContextValue;
218
+ /**
219
+ * Subscribe to processed spatial data.
220
+ * Batches updates to requestAnimationFrame to avoid re-rendering at device rate.
221
+ */
222
+ declare function useSpatialData(): SpatialData | null;
223
+ /**
224
+ * Subscribe to raw (pre-transform) spatial data.
225
+ * Batches updates to requestAnimationFrame.
226
+ */
227
+ declare function useRawSpatialData(): SpatialData | null;
228
+ /** Subscribe to button events via callback. Does not trigger re-renders. */
229
+ declare function useButtonEvent(callback: (event: ButtonEvent) => void): void;
230
+
231
+ interface ConnectionStatusProps {
232
+ className?: string;
233
+ }
234
+ declare function ConnectionStatus({ className }: ConnectionStatusProps): react_jsx_runtime.JSX.Element;
235
+
236
+ interface DeviceInfoProps {
237
+ className?: string;
238
+ /** Timeout in ms before showing "no device". Default: 5000 */
239
+ timeout?: number;
240
+ }
241
+ declare function DeviceInfo({ className, timeout }: DeviceInfoProps): react_jsx_runtime.JSX.Element;
242
+
243
+ interface SettingsPanelProps {
244
+ className?: string;
245
+ }
246
+ declare function SettingsPanel({ className }: SettingsPanelProps): react_jsx_runtime.JSX.Element;
247
+
248
+ interface DebugPanelProps {
249
+ className?: string;
250
+ }
251
+ declare function DebugPanel({ className }: DebugPanelProps): react_jsx_runtime.JSX.Element;
252
+
253
+ export { ConnectionStatus, type ConnectionStatusProps, DebugPanel, type DebugPanelProps, DeviceInfo, type DeviceInfoProps, SatMouseContext, type SatMouseContextValue, SatMouseProvider, type SatMouseProviderProps, SettingsPanel, type SettingsPanelProps, useButtonEvent, useRawSpatialData, useSatMouse, useSpatialData };
@@ -0,0 +1,253 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+ import { ReactNode } from 'react';
4
+
5
+ /** Minimal typed event emitter — no node:events dependency for browser compatibility */
6
+ declare class TypedEmitter<Events> {
7
+ private listeners;
8
+ on<K extends string & keyof Events>(event: K, listener: Events[K] & Function): this;
9
+ off<K extends string & keyof Events>(event: K, listener: Events[K] & Function): this;
10
+ protected emit<K extends string & keyof Events>(event: K, ...args: Events[K] extends (...a: infer A) => void ? A : never): void;
11
+ removeAllListeners(): void;
12
+ }
13
+
14
+ interface Vec3 {
15
+ x: number;
16
+ y: number;
17
+ z: number;
18
+ }
19
+ /** 6DOF spatial input frame — matches spatial-data.schema.json */
20
+ interface SpatialData {
21
+ translation: Vec3;
22
+ rotation: Vec3;
23
+ timestamp: number;
24
+ /** Source device ID (e.g., "cnx-c635", "hid-054c-5c4") */
25
+ deviceId?: string;
26
+ }
27
+ /** Button press/release event — matches button-event.schema.json */
28
+ interface ButtonEvent {
29
+ button: number;
30
+ pressed: boolean;
31
+ timestamp: number;
32
+ }
33
+ /** Device metadata from the bridge */
34
+ interface DeviceInfo$1 {
35
+ id: string;
36
+ name: string;
37
+ model?: string;
38
+ vendor?: string;
39
+ vendorId?: number;
40
+ productId?: number;
41
+ connectionType?: "usb" | "wireless" | "bluetooth" | "unknown";
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;
49
+ }
50
+ type ConnectionState = "disconnected" | "connecting" | "connected" | "failed";
51
+ type TransportProtocol = "webtransport" | "websocket" | "none";
52
+ interface SatMouseEvents {
53
+ spatialData: (data: SpatialData) => void;
54
+ buttonEvent: (data: ButtonEvent) => void;
55
+ stateChange: (state: ConnectionState, protocol: TransportProtocol) => void;
56
+ deviceStatus: (event: "connected" | "disconnected", device: DeviceInfo$1) => void;
57
+ error: (error: Error) => void;
58
+ }
59
+ interface ConnectOptions {
60
+ /**
61
+ * SatMouse URI: satmouse://connect?host=<ip>&wsPort=<port>&wtPort=<port>
62
+ * When provided, host/ports are extracted and used for discovery + transport.
63
+ * All params are optional — defaults to localhost:4444/4443.
64
+ */
65
+ uri?: string;
66
+ /** URL to td.json. Defaults to /td.json relative to window.location */
67
+ tdUrl?: string;
68
+ /** Direct WebSocket URL (skips discovery) */
69
+ wsUrl?: string;
70
+ /** Direct WebTransport URL (skips discovery) */
71
+ wtUrl?: string;
72
+ /** Certificate hash for self-signed WebTransport certs (base64) */
73
+ certHash?: string;
74
+ /** Preferred transport order. Default: ["webtransport", "websocket"] */
75
+ transports?: TransportProtocol[];
76
+ /** Auto-reconnect delay in ms. 0 to disable. Default: 2000 */
77
+ reconnectDelay?: number;
78
+ /** Max reconnect attempts before giving up. Default: 3 */
79
+ maxRetries?: number;
80
+ /** WebSocket subprotocol. Default: "satmouse-json" */
81
+ wsSubprotocol?: "satmouse-json" | "satmouse-binary";
82
+ }
83
+
84
+ /**
85
+ * Core connection to a SatMouse bridge.
86
+ *
87
+ * Handles discovery (via WoT Thing Description), transport negotiation
88
+ * (WebTransport with WebSocket fallback), event dispatch, and auto-reconnect.
89
+ */
90
+ declare class SatMouseConnection extends TypedEmitter<SatMouseEvents> {
91
+ private options;
92
+ private transport;
93
+ private reconnectTimer;
94
+ private intentionalClose;
95
+ private deviceInfoUrl;
96
+ private retryCount;
97
+ private _state;
98
+ private _protocol;
99
+ get state(): ConnectionState;
100
+ get protocol(): TransportProtocol;
101
+ constructor(options?: ConnectOptions);
102
+ connect(): Promise<void>;
103
+ /** Reset retry count and reconnect. Use after "failed" state. */
104
+ retry(): void;
105
+ disconnect(): void;
106
+ fetchDeviceInfo(): Promise<DeviceInfo$1[]>;
107
+ private tryTransport;
108
+ private setState;
109
+ private scheduleReconnect;
110
+ private clearReconnect;
111
+ }
112
+
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 {
117
+ source: InputAxis;
118
+ target: InputAxis;
119
+ /** Negate the value (default: false) */
120
+ flip?: boolean;
121
+ }
122
+
123
+ /** Per-device configuration */
124
+ interface DeviceConfig {
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. */
130
+ deadZone?: number;
131
+ /** Only pass the strongest axis, zero all others */
132
+ dominant?: boolean;
133
+ }
134
+ /** Global configuration */
135
+ interface InputConfig {
136
+ /** Default axis routes (used when device has no override) */
137
+ routes: AxisRoute[];
138
+ /** Default scale */
139
+ scale: number;
140
+ /** Dead zone threshold */
141
+ deadZone: number;
142
+ /** Dominant axis mode */
143
+ dominant: boolean;
144
+ /** Lock translation to zero */
145
+ lockPosition: boolean;
146
+ /** Lock rotation to zero */
147
+ lockRotation: boolean;
148
+ /** Per-device overrides keyed by device ID or pattern (e.g., "cnx-*") */
149
+ devices: Record<string, DeviceConfig>;
150
+ }
151
+
152
+ interface StorageAdapter {
153
+ getItem(key: string): string | null;
154
+ setItem(key: string, value: string): void;
155
+ }
156
+
157
+ interface InputManagerEvents {
158
+ spatialData: (data: SpatialData) => void;
159
+ rawSpatialData: (data: SpatialData) => void;
160
+ buttonEvent: (data: ButtonEvent) => void;
161
+ stateChange: (state: ConnectionState, protocol: TransportProtocol) => void;
162
+ deviceStatus: (event: "connected" | "disconnected", device: DeviceInfo$1) => void;
163
+ configChange: (config: InputConfig) => void;
164
+ }
165
+ interface DeviceWithConfig {
166
+ device: DeviceInfo$1;
167
+ config: DeviceConfig;
168
+ }
169
+ declare class InputManager extends TypedEmitter<InputManagerEvents> {
170
+ private connections;
171
+ private storage?;
172
+ private knownDevices;
173
+ private deviceAccumulators;
174
+ private accDirty;
175
+ private flushTimer;
176
+ private _config;
177
+ get config(): InputConfig;
178
+ constructor(config?: Partial<InputConfig>, storage?: StorageAdapter);
179
+ addConnection(connection: SatMouseConnection): void;
180
+ removeConnection(connection: SatMouseConnection): void;
181
+ connect(): Promise<void>;
182
+ disconnect(): void;
183
+ fetchDeviceInfo(): Promise<DeviceInfo$1[]>;
184
+ getDevicesWithConfig(): DeviceWithConfig[];
185
+ getDeviceConfig(deviceId: string): DeviceConfig;
186
+ updateConfig(partial: Partial<InputConfig>, persist?: boolean): void;
187
+ updateDeviceConfig(deviceId: string, partial: DeviceConfig, persist?: boolean): void;
188
+ resetDeviceConfig(deviceId: string, persist?: boolean): void;
189
+ resetAllConfig(): void;
190
+ onSpatialData(callback: (data: SpatialData) => void): () => void;
191
+ onButtonEvent(callback: (data: ButtonEvent) => void): () => void;
192
+ private wireConnection;
193
+ private flushAccumulator;
194
+ /** Per-device: deadZone → dominant → routes (flip + scale + remap in one pass) */
195
+ private processPerDevice;
196
+ /** Get the effective routes for a device: device config override > device axes metadata > global default */
197
+ private resolveRoutes;
198
+ }
199
+
200
+ interface SatMouseContextValue {
201
+ manager: InputManager;
202
+ state: ConnectionState;
203
+ protocol: TransportProtocol;
204
+ config: InputConfig;
205
+ updateConfig: (partial: Partial<InputConfig>) => void;
206
+ }
207
+ declare const SatMouseContext: react.Context<SatMouseContextValue | null>;
208
+ interface SatMouseProviderProps {
209
+ connectOptions?: ConnectOptions;
210
+ config?: Partial<InputConfig>;
211
+ autoConnect?: boolean;
212
+ children: ReactNode;
213
+ }
214
+ declare function SatMouseProvider({ connectOptions, config: configOverrides, autoConnect, children, }: SatMouseProviderProps): react_jsx_runtime.JSX.Element;
215
+
216
+ /** Access the SatMouse context. Throws if used outside SatMouseProvider. */
217
+ declare function useSatMouse(): SatMouseContextValue;
218
+ /**
219
+ * Subscribe to processed spatial data.
220
+ * Batches updates to requestAnimationFrame to avoid re-rendering at device rate.
221
+ */
222
+ declare function useSpatialData(): SpatialData | null;
223
+ /**
224
+ * Subscribe to raw (pre-transform) spatial data.
225
+ * Batches updates to requestAnimationFrame.
226
+ */
227
+ declare function useRawSpatialData(): SpatialData | null;
228
+ /** Subscribe to button events via callback. Does not trigger re-renders. */
229
+ declare function useButtonEvent(callback: (event: ButtonEvent) => void): void;
230
+
231
+ interface ConnectionStatusProps {
232
+ className?: string;
233
+ }
234
+ declare function ConnectionStatus({ className }: ConnectionStatusProps): react_jsx_runtime.JSX.Element;
235
+
236
+ interface DeviceInfoProps {
237
+ className?: string;
238
+ /** Timeout in ms before showing "no device". Default: 5000 */
239
+ timeout?: number;
240
+ }
241
+ declare function DeviceInfo({ className, timeout }: DeviceInfoProps): react_jsx_runtime.JSX.Element;
242
+
243
+ interface SettingsPanelProps {
244
+ className?: string;
245
+ }
246
+ declare function SettingsPanel({ className }: SettingsPanelProps): react_jsx_runtime.JSX.Element;
247
+
248
+ interface DebugPanelProps {
249
+ className?: string;
250
+ }
251
+ declare function DebugPanel({ className }: DebugPanelProps): react_jsx_runtime.JSX.Element;
252
+
253
+ export { ConnectionStatus, type ConnectionStatusProps, DebugPanel, type DebugPanelProps, DeviceInfo, type DeviceInfoProps, SatMouseContext, type SatMouseContextValue, SatMouseProvider, type SatMouseProviderProps, SettingsPanel, type SettingsPanelProps, useButtonEvent, useRawSpatialData, useSatMouse, useSpatialData };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kelnishi/satmouse-client",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "description": "Client SDK for SatMouse 6DOF spatial input bridge",
5
5
  "type": "module",
6
6
  "sideEffects": ["./src/elements/*.ts", "./dist/elements/*"],