@kelnishi/satmouse-client 0.10.9 → 0.12.3

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.
@@ -1,5 +1,5 @@
1
1
  // src/utils/action-map.ts
2
- var FULL_AXES = ["tx", "ty", "tz", "rx", "ry", "rz"];
2
+ var FULL_AXES = ["tx", "ty", "tz", "rx", "ry", "rz", "w"];
3
3
  var DEFAULT_ROUTES = [
4
4
  { source: "tx", target: "tx" },
5
5
  { source: "ty", target: "ty" },
@@ -30,31 +30,38 @@ function readAxis(data, axis) {
30
30
  return data.rotation.y;
31
31
  case "rz":
32
32
  return data.rotation.z;
33
+ case "w":
34
+ return data.w ?? 0;
33
35
  default:
34
36
  return 0;
35
37
  }
36
38
  }
37
- function writeAxis(t, r, axis, value) {
39
+ function writeAxis(acc, axis, value) {
38
40
  const isNeg = axis.endsWith("-");
39
41
  const base = axis.replace(/[+-]$/, "");
40
42
  const sign = isNeg ? -1 : 1;
43
+ if (base === "w") {
44
+ acc.w += value * sign;
45
+ return;
46
+ }
41
47
  const group = base[0];
42
48
  const key = base[1];
43
- if (group === "t") t[key] += value * sign;
44
- else r[key] += value * sign;
49
+ if (group === "t") acc.t[key] += value * sign;
50
+ else acc.r[key] += value * sign;
45
51
  }
46
- function applyRoutes(data, routes, scale = 1) {
47
- const t = { x: 0, y: 0, z: 0 };
48
- const r = { x: 0, y: 0, z: 0 };
52
+ function applyRoutes(data, routes, translateScale = 1, rotateScale = 1, wScale = 1) {
53
+ const acc = { t: { x: 0, y: 0, z: 0 }, r: { x: 0, y: 0, z: 0 }, w: 0 };
49
54
  for (const route of routes) {
50
55
  let value = readAxis(data, route.source);
51
56
  if (route.flip) value = -value;
57
+ const targetBase = route.target.replace(/[+-]$/, "");
58
+ const scale = targetBase === "w" ? wScale : targetBase[0] === "t" ? translateScale : rotateScale;
52
59
  value *= scale;
53
- writeAxis(t, r, route.target, value);
60
+ writeAxis(acc, route.target, value);
54
61
  }
55
- return { translation: t, rotation: r, timestamp: data.timestamp, deviceId: data.deviceId };
62
+ return { translation: acc.t, rotation: acc.r, w: acc.w || void 0, timestamp: data.timestamp, deviceId: data.deviceId };
56
63
  }
57
64
 
58
65
  export { DEFAULT_ROUTES, FULL_AXES, applyRoutes, buildRoutes };
59
- //# sourceMappingURL=chunk-RNM322RZ.js.map
60
- //# sourceMappingURL=chunk-RNM322RZ.js.map
66
+ //# sourceMappingURL=chunk-RE4PNORY.js.map
67
+ //# sourceMappingURL=chunk-RE4PNORY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/action-map.ts"],"names":[],"mappings":"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","file":"chunk-RE4PNORY.js","sourcesContent":["import type { SpatialData } from \"../core/types.js\";\n\n/** Axis identifier — full or half */\nexport type InputAxis =\n | \"tx\" | \"ty\" | \"tz\" | \"rx\" | \"ry\" | \"rz\" | \"w\"\n | \"tx+\" | \"ty+\" | \"tz+\" | \"rx+\" | \"ry+\" | \"rz+\" | \"w+\"\n | \"tx-\" | \"ty-\" | \"tz-\" | \"rx-\" | \"ry-\" | \"rz-\" | \"w-\";\n\n/** The 7 full output axes (6DOF + W) */\nexport const FULL_AXES: InputAxis[] = [\"tx\", \"ty\", \"tz\", \"rx\", \"ry\", \"rz\", \"w\"];\n\n/** A single axis route — reads from source, writes to target */\nexport interface AxisRoute {\n source: InputAxis;\n target: InputAxis;\n /** Negate the value (default: false) */\n flip?: boolean;\n}\n\n/** Default 6DOF passthrough */\nexport const DEFAULT_ROUTES: AxisRoute[] = [\n { source: \"tx\", target: \"tx\" },\n { source: \"ty\", target: \"ty\" },\n { source: \"tz\", target: \"tz\" },\n { source: \"rx\", target: \"rx\" },\n { source: \"ry\", target: \"ry\" },\n { source: \"rz\", target: \"rz\" },\n];\n\n/** Build passthrough routes from a device's axis list. Half-axes target their base axis.\n * Negative half-axes (e.g., \"ty-\") get flip: true. */\nexport function buildRoutes(axes: string[]): AxisRoute[] {\n return axes.map((axis) => {\n const base = axis.replace(/[+-]$/, \"\");\n const flip = axis.endsWith(\"-\");\n return { source: axis as InputAxis, target: base as InputAxis, ...(flip && { flip: true }) };\n });\n}\n\n/** Read a value from SpatialData by axis name. Half-axes read the same as the full axis. */\nfunction readAxis(data: SpatialData, axis: InputAxis): number {\n const base = axis.replace(/[+-]$/, \"\");\n switch (base) {\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 case \"w\": return data.w ?? 0;\n default: return 0;\n }\n}\n\n/** Accumulator for applyRoutes */\ninterface RouteAccum {\n t: { x: number; y: number; z: number };\n r: { x: number; y: number; z: number };\n w: number;\n}\n\n/** Write a value to accumulators. Half-axis targets: \"tz+\" adds, \"tz-\" subtracts. */\nfunction writeAxis(acc: RouteAccum, axis: InputAxis, value: number): void {\n const isNeg = axis.endsWith(\"-\");\n const base = axis.replace(/[+-]$/, \"\");\n const sign = isNeg ? -1 : 1;\n if (base === \"w\") { acc.w += value * sign; return; }\n const group = base[0] as \"t\" | \"r\";\n const key = base[1] as \"x\" | \"y\" | \"z\";\n if (group === \"t\") acc.t[key] += value * sign;\n else acc.r[key] += value * sign;\n}\n\n/** Apply routes to SpatialData. Multiple routes targeting the same axis accumulate. */\nexport function applyRoutes(data: SpatialData, routes: AxisRoute[], translateScale = 1, rotateScale = 1, wScale = 1): SpatialData {\n const acc: RouteAccum = { t: { x: 0, y: 0, z: 0 }, r: { x: 0, y: 0, z: 0 }, w: 0 };\n\n for (const route of routes) {\n let value = readAxis(data, route.source);\n if (route.flip) value = -value;\n const targetBase = route.target.replace(/[+-]$/, \"\");\n const scale = targetBase === \"w\" ? wScale : targetBase[0] === \"t\" ? translateScale : rotateScale;\n value *= scale;\n writeAxis(acc, route.target, value);\n }\n\n return { translation: acc.t, rotation: acc.r, w: acc.w || undefined, timestamp: data.timestamp, deviceId: data.deviceId };\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  // src/utils/action-map.ts
4
- var FULL_AXES = ["tx", "ty", "tz", "rx", "ry", "rz"];
4
+ var FULL_AXES = ["tx", "ty", "tz", "rx", "ry", "rz", "w"];
5
5
  var DEFAULT_ROUTES = [
6
6
  { source: "tx", target: "tx" },
7
7
  { source: "ty", target: "ty" },
@@ -32,34 +32,41 @@ function readAxis(data, axis) {
32
32
  return data.rotation.y;
33
33
  case "rz":
34
34
  return data.rotation.z;
35
+ case "w":
36
+ return data.w ?? 0;
35
37
  default:
36
38
  return 0;
37
39
  }
38
40
  }
39
- function writeAxis(t, r, axis, value) {
41
+ function writeAxis(acc, axis, value) {
40
42
  const isNeg = axis.endsWith("-");
41
43
  const base = axis.replace(/[+-]$/, "");
42
44
  const sign = isNeg ? -1 : 1;
45
+ if (base === "w") {
46
+ acc.w += value * sign;
47
+ return;
48
+ }
43
49
  const group = base[0];
44
50
  const key = base[1];
45
- if (group === "t") t[key] += value * sign;
46
- else r[key] += value * sign;
51
+ if (group === "t") acc.t[key] += value * sign;
52
+ else acc.r[key] += value * sign;
47
53
  }
48
- function applyRoutes(data, routes, scale = 1) {
49
- const t = { x: 0, y: 0, z: 0 };
50
- const r = { x: 0, y: 0, z: 0 };
54
+ function applyRoutes(data, routes, translateScale = 1, rotateScale = 1, wScale = 1) {
55
+ const acc = { t: { x: 0, y: 0, z: 0 }, r: { x: 0, y: 0, z: 0 }, w: 0 };
51
56
  for (const route of routes) {
52
57
  let value = readAxis(data, route.source);
53
58
  if (route.flip) value = -value;
59
+ const targetBase = route.target.replace(/[+-]$/, "");
60
+ const scale = targetBase === "w" ? wScale : targetBase[0] === "t" ? translateScale : rotateScale;
54
61
  value *= scale;
55
- writeAxis(t, r, route.target, value);
62
+ writeAxis(acc, route.target, value);
56
63
  }
57
- return { translation: t, rotation: r, timestamp: data.timestamp, deviceId: data.deviceId };
64
+ return { translation: acc.t, rotation: acc.r, w: acc.w || void 0, timestamp: data.timestamp, deviceId: data.deviceId };
58
65
  }
59
66
 
60
67
  exports.DEFAULT_ROUTES = DEFAULT_ROUTES;
61
68
  exports.FULL_AXES = FULL_AXES;
62
69
  exports.applyRoutes = applyRoutes;
63
70
  exports.buildRoutes = buildRoutes;
64
- //# sourceMappingURL=chunk-JTG5GEIB.cjs.map
65
- //# sourceMappingURL=chunk-JTG5GEIB.cjs.map
71
+ //# sourceMappingURL=chunk-Y75556IA.cjs.map
72
+ //# sourceMappingURL=chunk-Y75556IA.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/action-map.ts"],"names":[],"mappings":"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","file":"chunk-Y75556IA.cjs","sourcesContent":["import type { SpatialData } from \"../core/types.js\";\n\n/** Axis identifier — full or half */\nexport type InputAxis =\n | \"tx\" | \"ty\" | \"tz\" | \"rx\" | \"ry\" | \"rz\" | \"w\"\n | \"tx+\" | \"ty+\" | \"tz+\" | \"rx+\" | \"ry+\" | \"rz+\" | \"w+\"\n | \"tx-\" | \"ty-\" | \"tz-\" | \"rx-\" | \"ry-\" | \"rz-\" | \"w-\";\n\n/** The 7 full output axes (6DOF + W) */\nexport const FULL_AXES: InputAxis[] = [\"tx\", \"ty\", \"tz\", \"rx\", \"ry\", \"rz\", \"w\"];\n\n/** A single axis route — reads from source, writes to target */\nexport interface AxisRoute {\n source: InputAxis;\n target: InputAxis;\n /** Negate the value (default: false) */\n flip?: boolean;\n}\n\n/** Default 6DOF passthrough */\nexport const DEFAULT_ROUTES: AxisRoute[] = [\n { source: \"tx\", target: \"tx\" },\n { source: \"ty\", target: \"ty\" },\n { source: \"tz\", target: \"tz\" },\n { source: \"rx\", target: \"rx\" },\n { source: \"ry\", target: \"ry\" },\n { source: \"rz\", target: \"rz\" },\n];\n\n/** Build passthrough routes from a device's axis list. Half-axes target their base axis.\n * Negative half-axes (e.g., \"ty-\") get flip: true. */\nexport function buildRoutes(axes: string[]): AxisRoute[] {\n return axes.map((axis) => {\n const base = axis.replace(/[+-]$/, \"\");\n const flip = axis.endsWith(\"-\");\n return { source: axis as InputAxis, target: base as InputAxis, ...(flip && { flip: true }) };\n });\n}\n\n/** Read a value from SpatialData by axis name. Half-axes read the same as the full axis. */\nfunction readAxis(data: SpatialData, axis: InputAxis): number {\n const base = axis.replace(/[+-]$/, \"\");\n switch (base) {\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 case \"w\": return data.w ?? 0;\n default: return 0;\n }\n}\n\n/** Accumulator for applyRoutes */\ninterface RouteAccum {\n t: { x: number; y: number; z: number };\n r: { x: number; y: number; z: number };\n w: number;\n}\n\n/** Write a value to accumulators. Half-axis targets: \"tz+\" adds, \"tz-\" subtracts. */\nfunction writeAxis(acc: RouteAccum, axis: InputAxis, value: number): void {\n const isNeg = axis.endsWith(\"-\");\n const base = axis.replace(/[+-]$/, \"\");\n const sign = isNeg ? -1 : 1;\n if (base === \"w\") { acc.w += value * sign; return; }\n const group = base[0] as \"t\" | \"r\";\n const key = base[1] as \"x\" | \"y\" | \"z\";\n if (group === \"t\") acc.t[key] += value * sign;\n else acc.r[key] += value * sign;\n}\n\n/** Apply routes to SpatialData. Multiple routes targeting the same axis accumulate. */\nexport function applyRoutes(data: SpatialData, routes: AxisRoute[], translateScale = 1, rotateScale = 1, wScale = 1): SpatialData {\n const acc: RouteAccum = { t: { x: 0, y: 0, z: 0 }, r: { x: 0, y: 0, z: 0 }, w: 0 };\n\n for (const route of routes) {\n let value = readAxis(data, route.source);\n if (route.flip) value = -value;\n const targetBase = route.target.replace(/[+-]$/, \"\");\n const scale = targetBase === \"w\" ? wScale : targetBase[0] === \"t\" ? translateScale : rotateScale;\n value *= scale;\n writeAxis(acc, route.target, value);\n }\n\n return { translation: acc.t, rotation: acc.r, w: acc.w || undefined, timestamp: data.timestamp, deviceId: data.deviceId };\n}\n"]}
@@ -12,10 +12,12 @@ interface Vec3 {
12
12
  y: number;
13
13
  z: number;
14
14
  }
15
- /** 6DOF spatial input frame — matches spatial-data.schema.json */
15
+ /** 6DOF+W spatial input frame */
16
16
  interface SpatialData {
17
17
  translation: Vec3;
18
18
  rotation: Vec3;
19
+ /** Virtual W axis — application-defined (e.g., zoom, scroll, tool size) */
20
+ w?: number;
19
21
  timestamp: number;
20
22
  /** Source device ID (e.g., "cnx-c635", "hid-054c-5c4") */
21
23
  deviceId?: string;
@@ -26,6 +28,8 @@ interface ButtonEvent {
26
28
  pressed: boolean;
27
29
  timestamp: number;
28
30
  }
31
+ /** Device form factor */
32
+ type DeviceClass = "spacemouse" | "gamepad" | "dial" | "joystick" | "6dof" | "other";
29
33
  /** Device metadata from the bridge */
30
34
  interface DeviceInfo {
31
35
  id: string;
@@ -35,6 +39,8 @@ interface DeviceInfo {
35
39
  vendorId?: number;
36
40
  productId?: number;
37
41
  connectionType?: "usb" | "wireless" | "bluetooth" | "unknown";
42
+ /** General form factor */
43
+ deviceClass?: DeviceClass;
38
44
  connected?: boolean;
39
45
  /** Axes this device provides (e.g., ["tx","ty","tz","rx","ry","rz"] or ["tx","ty","rx","ry","tz+","rz+"]) */
40
46
  axes?: string[];
@@ -42,6 +48,8 @@ interface DeviceInfo {
42
48
  axisLabels?: string[];
43
49
  /** Number of buttons this device provides */
44
50
  buttonCount?: number;
51
+ /** Human-readable labels for buttons (indexed by targetButton) */
52
+ buttonLabels?: string[];
45
53
  }
46
54
  type ConnectionState = "disconnected" | "connecting" | "connected" | "failed";
47
55
  type TransportProtocol = "webtransport" | "websocket" | "none";
@@ -12,10 +12,12 @@ interface Vec3 {
12
12
  y: number;
13
13
  z: number;
14
14
  }
15
- /** 6DOF spatial input frame — matches spatial-data.schema.json */
15
+ /** 6DOF+W spatial input frame */
16
16
  interface SpatialData {
17
17
  translation: Vec3;
18
18
  rotation: Vec3;
19
+ /** Virtual W axis — application-defined (e.g., zoom, scroll, tool size) */
20
+ w?: number;
19
21
  timestamp: number;
20
22
  /** Source device ID (e.g., "cnx-c635", "hid-054c-5c4") */
21
23
  deviceId?: string;
@@ -26,6 +28,8 @@ interface ButtonEvent {
26
28
  pressed: boolean;
27
29
  timestamp: number;
28
30
  }
31
+ /** Device form factor */
32
+ type DeviceClass = "spacemouse" | "gamepad" | "dial" | "joystick" | "6dof" | "other";
29
33
  /** Device metadata from the bridge */
30
34
  interface DeviceInfo {
31
35
  id: string;
@@ -35,6 +39,8 @@ interface DeviceInfo {
35
39
  vendorId?: number;
36
40
  productId?: number;
37
41
  connectionType?: "usb" | "wireless" | "bluetooth" | "unknown";
42
+ /** General form factor */
43
+ deviceClass?: DeviceClass;
38
44
  connected?: boolean;
39
45
  /** Axes this device provides (e.g., ["tx","ty","tz","rx","ry","rz"] or ["tx","ty","rx","ry","tz+","rz+"]) */
40
46
  axes?: string[];
@@ -42,6 +48,8 @@ interface DeviceInfo {
42
48
  axisLabels?: string[];
43
49
  /** Number of buttons this device provides */
44
50
  buttonCount?: number;
51
+ /** Human-readable labels for buttons (indexed by targetButton) */
52
+ buttonLabels?: string[];
45
53
  }
46
54
  type ConnectionState = "disconnected" | "connecting" | "connected" | "failed";
47
55
  type TransportProtocol = "webtransport" | "websocket" | "none";
@@ -1,5 +1,5 @@
1
- import { T as ThingDescription, S as SpatialData, B as ButtonEvent } from '../connection-DQxI5qib.cjs';
2
- export { C as ConnectOptions, a as ConnectionState, D as DeviceInfo, b as SatMouseConnection, c as SatMouseEvents, d as TransportProtocol, e as TypedEmitter, V as Vec3, f as buildSatMouseUri, p as parseSatMouseUri } from '../connection-DQxI5qib.cjs';
1
+ import { T as ThingDescription, S as SpatialData, B as ButtonEvent } from '../connection-iUlge6Er.cjs';
2
+ export { C as ConnectOptions, a as ConnectionState, D as DeviceInfo, b as SatMouseConnection, c as SatMouseEvents, d as TransportProtocol, e as TypedEmitter, V as Vec3, f as buildSatMouseUri, p as parseSatMouseUri } from '../connection-iUlge6Er.cjs';
3
3
 
4
4
  interface ResolvedEndpoints {
5
5
  webtransport?: {
@@ -1,5 +1,5 @@
1
- import { T as ThingDescription, S as SpatialData, B as ButtonEvent } from '../connection-DQxI5qib.js';
2
- export { C as ConnectOptions, a as ConnectionState, D as DeviceInfo, b as SatMouseConnection, c as SatMouseEvents, d as TransportProtocol, e as TypedEmitter, V as Vec3, f as buildSatMouseUri, p as parseSatMouseUri } from '../connection-DQxI5qib.js';
1
+ import { T as ThingDescription, S as SpatialData, B as ButtonEvent } from '../connection-iUlge6Er.js';
2
+ export { C as ConnectOptions, a as ConnectionState, D as DeviceInfo, b as SatMouseConnection, c as SatMouseEvents, d as TransportProtocol, e as TypedEmitter, V as Vec3, f as buildSatMouseUri, p as parseSatMouseUri } from '../connection-iUlge6Er.js';
3
3
 
4
4
  interface ResolvedEndpoints {
5
5
  webtransport?: {
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkJTG5GEIB_cjs = require('../chunk-JTG5GEIB.cjs');
3
+ var chunkY75556IA_cjs = require('../chunk-Y75556IA.cjs');
4
4
 
5
5
  // src/elements/registry.ts
6
6
  var globalManager = null;
@@ -161,6 +161,19 @@ var STYLES = `
161
161
  .reset-btn { background: none; border: 1px solid #1a4a8a; border-radius: 3px; color: #7f8c8d;
162
162
  font-size: 11px; padding: 3px 8px; cursor: pointer; margin-top: 4px; }
163
163
  .reset-btn:hover { color: #e0e0e0; border-color: #e74c3c; }
164
+ .btn-section { display: flex; flex-direction: column; gap: 4px; }
165
+ .btn-section-label { color: #7f8c8d; font-weight: 600; font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px; }
166
+ .btn-route { display: flex; gap: 6px; align-items: center; font-size: 11px; }
167
+ .btn-route .btn-idx { color: #7f8c8d; font-family: monospace; min-width: 32px; }
168
+ .btn-route .btn-arrow { color: #7f8c8d; }
169
+ .btn-route .btn-key { color: #3498db; font-family: monospace; }
170
+ .btn-route .btn-remove { cursor: pointer; color: #e74c3c; background: none; border: none;
171
+ font-size: 11px; padding: 0 2px; font-family: inherit; }
172
+ .btn-route .btn-remove:hover { color: #ff6b6b; }
173
+ .btn-add { background: none; border: 1px dashed #1a4a8a; border-radius: 3px; color: #7f8c8d;
174
+ font-size: 11px; padding: 4px 8px; cursor: pointer; font-family: inherit; }
175
+ .btn-add:hover { color: #e0e0e0; border-color: #3498db; }
176
+ .btn-add.listening { color: #f39c12; border-color: #f39c12; border-style: solid; cursor: default; }
164
177
  </style>
165
178
  `;
166
179
  function mapSlider(v) {
@@ -258,7 +271,7 @@ var SatMouseDevices = class extends HTMLElement {
258
271
  label.textContent = device.axisLabels?.[i] ?? deviceAxes[i].toUpperCase();
259
272
  row.appendChild(label);
260
273
  const sel = document.createElement("select");
261
- for (const target of chunkJTG5GEIB_cjs.FULL_AXES) {
274
+ for (const target of chunkY75556IA_cjs.FULL_AXES) {
262
275
  const opt = document.createElement("option");
263
276
  opt.value = target;
264
277
  opt.textContent = target.toUpperCase();
@@ -272,18 +285,93 @@ var SatMouseDevices = class extends HTMLElement {
272
285
  routeGroup.appendChild(row);
273
286
  }
274
287
  controls.appendChild(routeGroup);
275
- const sensRow = document.createElement("div");
276
- sensRow.className = "slider-row";
277
- const currentScale = cfg.scale ?? mgr.config.scale;
278
- sensRow.innerHTML = `<label>Scale</label><input type="range" min="0" max="100" value="${Math.round(unmapSlider(currentScale))}"><span>${currentScale.toFixed(4)}</span>`;
279
- const slider = sensRow.querySelector("input");
280
- const span = sensRow.querySelector("span");
281
- slider.addEventListener("input", () => {
282
- const v = mapSlider(+slider.value);
283
- span.textContent = v.toFixed(4);
284
- mgr.updateDeviceConfig(device.id, { scale: v });
288
+ for (const [label, key, globalKey] of [
289
+ ["Trans", "translateScale", "translateScale"],
290
+ ["Rot", "rotateScale", "rotateScale"],
291
+ ["W", "wScale", "wScale"]
292
+ ]) {
293
+ const row = document.createElement("div");
294
+ row.className = "slider-row";
295
+ const val = cfg[key] ?? mgr.config[globalKey];
296
+ row.innerHTML = `<label>${label}</label><input type="range" min="0" max="100" value="${Math.round(unmapSlider(val))}"><span>${val.toFixed(4)}</span>`;
297
+ const sl = row.querySelector("input");
298
+ const sp = row.querySelector("span");
299
+ sl.addEventListener("input", () => {
300
+ const v = mapSlider(+sl.value);
301
+ sp.textContent = v.toFixed(4);
302
+ mgr.updateDeviceConfig(device.id, { [key]: v });
303
+ });
304
+ controls.appendChild(row);
305
+ }
306
+ const btnSection = document.createElement("div");
307
+ btnSection.className = "btn-section";
308
+ const btnLabel = document.createElement("div");
309
+ btnLabel.className = "btn-section-label";
310
+ btnLabel.textContent = "Button Mappings";
311
+ btnSection.appendChild(btnLabel);
312
+ const buttonRoutes = cfg.buttonRoutes ?? [];
313
+ const labels = device.buttonLabels ?? [];
314
+ for (let i = 0; i < buttonRoutes.length; i++) {
315
+ const route = buttonRoutes[i];
316
+ const btnName = labels[route.button] ?? `Btn ${route.button}`;
317
+ const row = document.createElement("div");
318
+ row.className = "btn-route";
319
+ const idxSpan = document.createElement("span");
320
+ idxSpan.className = "btn-idx";
321
+ idxSpan.textContent = btnName;
322
+ row.appendChild(idxSpan);
323
+ const arrow = document.createElement("span");
324
+ arrow.className = "btn-arrow";
325
+ arrow.textContent = "\u2192";
326
+ row.appendChild(arrow);
327
+ const keySpan = document.createElement("span");
328
+ keySpan.className = "btn-key";
329
+ keySpan.textContent = route.key;
330
+ row.appendChild(keySpan);
331
+ const editBtn = document.createElement("button");
332
+ editBtn.className = "btn-remove";
333
+ editBtn.textContent = "\u270E";
334
+ editBtn.title = "Remap key";
335
+ const routeIdx = i;
336
+ editBtn.addEventListener("click", () => {
337
+ keySpan.textContent = "Press a key...";
338
+ keySpan.style.color = "#f39c12";
339
+ const onKey = (e) => {
340
+ e.preventDefault();
341
+ e.stopPropagation();
342
+ document.removeEventListener("keydown", onKey, true);
343
+ const current = mgr.getDeviceConfig(device.id).buttonRoutes ?? [];
344
+ const updated = current.map(
345
+ (r, j) => j === routeIdx ? { ...r, key: e.key, code: e.code } : r
346
+ );
347
+ mgr.updateDeviceConfig(device.id, { buttonRoutes: updated });
348
+ this.refreshControls(panel, device);
349
+ };
350
+ document.addEventListener("keydown", onKey, true);
351
+ });
352
+ row.appendChild(editBtn);
353
+ const removeBtn = document.createElement("button");
354
+ removeBtn.className = "btn-remove";
355
+ removeBtn.textContent = "\xD7";
356
+ removeBtn.title = "Remove";
357
+ removeBtn.addEventListener("click", () => {
358
+ const current = mgr.getDeviceConfig(device.id).buttonRoutes ?? [];
359
+ const updated = current.filter((_, j) => j !== routeIdx);
360
+ mgr.updateDeviceConfig(device.id, { buttonRoutes: updated });
361
+ this.refreshControls(panel, device);
362
+ });
363
+ row.appendChild(removeBtn);
364
+ btnSection.appendChild(row);
365
+ }
366
+ const addBtn = document.createElement("button");
367
+ addBtn.className = "btn-add";
368
+ addBtn.textContent = "+ Add Button Mapping";
369
+ addBtn.addEventListener("click", () => {
370
+ if (addBtn.classList.contains("listening")) return;
371
+ this.startButtonListen(addBtn, mgr, device, panel);
285
372
  });
286
- controls.appendChild(sensRow);
373
+ btnSection.appendChild(addBtn);
374
+ controls.appendChild(btnSection);
287
375
  const resetBtn = document.createElement("button");
288
376
  resetBtn.className = "reset-btn";
289
377
  resetBtn.textContent = "Restore Defaults";
@@ -303,13 +391,49 @@ var SatMouseDevices = class extends HTMLElement {
303
391
  if (cfg.routes && Array.isArray(cfg.routes)) return cfg.routes;
304
392
  }
305
393
  }
306
- return chunkJTG5GEIB_cjs.buildRoutes(deviceAxes);
394
+ return chunkY75556IA_cjs.buildRoutes(deviceAxes);
307
395
  }
308
396
  updateRoute(deviceId, index, deviceAxes, patch) {
309
397
  const base = this.getRoutes(deviceId, deviceAxes);
310
398
  const updated = base.map((r, j) => j === index ? { ...r, ...patch } : { ...r });
311
399
  this.manager.updateDeviceConfig(deviceId, { routes: updated });
312
400
  }
401
+ startButtonListen(btn, mgr, device, panel) {
402
+ btn.classList.add("listening");
403
+ btn.textContent = "Press a device button...";
404
+ const onButton = (event) => {
405
+ if (!event.pressed) return;
406
+ mgr.off("buttonEvent", onButton);
407
+ const capturedButton = event.button;
408
+ btn.textContent = `Btn ${capturedButton} \u2192 Press a key...`;
409
+ const onKey = (e) => {
410
+ e.preventDefault();
411
+ e.stopPropagation();
412
+ document.removeEventListener("keydown", onKey, true);
413
+ const route = {
414
+ button: capturedButton,
415
+ key: e.key,
416
+ code: e.code
417
+ };
418
+ const current = mgr.getDeviceConfig(device.id).buttonRoutes ?? [];
419
+ const updated = current.filter((r) => r.button !== capturedButton);
420
+ updated.push(route);
421
+ mgr.updateDeviceConfig(device.id, { buttonRoutes: updated });
422
+ this.refreshControls(panel, device);
423
+ };
424
+ document.addEventListener("keydown", onKey, true);
425
+ };
426
+ mgr.on("buttonEvent", onButton);
427
+ const onCancel = (e) => {
428
+ if (e.key === "Escape") {
429
+ mgr.off("buttonEvent", onButton);
430
+ document.removeEventListener("keydown", onCancel, true);
431
+ btn.classList.remove("listening");
432
+ btn.textContent = "+ Add Button Mapping";
433
+ }
434
+ };
435
+ document.addEventListener("keydown", onCancel, true);
436
+ }
313
437
  removeDevice(device) {
314
438
  this.shadowRoot.getElementById(`dev-${device.id}`)?.remove();
315
439
  if (this.container.children.length === 0) {