Next.js app with a browser-side buttplug/buttplug-wasm control layer (server never touches real-time device commands), SQLite storage via Drizzle, single-secret auth, recordings/replay with device remapping, a usage stats dashboard, Docker deployment, and Gitea CI. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W8WkFF5ppURBAB918593Eb
47 lines
1.5 KiB
TypeScript
47 lines
1.5 KiB
TypeScript
import type { RecordingDeviceSlot } from "@/lib/db/schema";
|
|
import type { ConnectedDeviceInfo } from "./types";
|
|
|
|
export interface DeviceRemapEntry {
|
|
slotLabel: string;
|
|
recordedBleName: string;
|
|
matchedDeviceIndex: number | null;
|
|
}
|
|
|
|
/**
|
|
* Best-effort name match from a recording's saved device slots to the
|
|
* currently-connected devices. Web Bluetooth exposes no stable hardware id
|
|
* across sessions, so this is inherently approximate: two devices sharing an
|
|
* identical advertised name are indistinguishable and must be disambiguated
|
|
* manually in the remap UI - this isn't a bug, it's a hard BLE limitation.
|
|
*/
|
|
export function autoMapDeviceSlots(
|
|
slots: RecordingDeviceSlot[],
|
|
connectedDevices: ConnectedDeviceInfo[],
|
|
): DeviceRemapEntry[] {
|
|
const usedIndexes = new Set<number>();
|
|
|
|
return slots.map((slot) => {
|
|
const normalizedSlotName = slot.recordedBleName.trim().toLowerCase();
|
|
|
|
const exact = connectedDevices.find(
|
|
(d) => !usedIndexes.has(d.index) && d.name.trim().toLowerCase() === normalizedSlotName,
|
|
);
|
|
const partial =
|
|
exact ??
|
|
connectedDevices.find(
|
|
(d) =>
|
|
!usedIndexes.has(d.index) &&
|
|
(d.name.toLowerCase().includes(normalizedSlotName) ||
|
|
normalizedSlotName.includes(d.name.toLowerCase())),
|
|
);
|
|
|
|
if (partial) usedIndexes.add(partial.index);
|
|
|
|
return {
|
|
slotLabel: slot.slotLabel,
|
|
recordedBleName: slot.recordedBleName,
|
|
matchedDeviceIndex: partial?.index ?? null,
|
|
};
|
|
});
|
|
}
|