feat(controller): route status messages through notifications

- Send controller connected and disabled messages over a new IPC channel so they follow ankiConnect.behavior.notificationType
- Remove the separate controller status toast from the overlay
- Only announce connected controllers when controller.enabled is on
- Announce a controller again after it reconnects, and update usage docs
This commit is contained in:
sudacode committed 2026-11-03 21:55:55 -08:00
1 parent 1c50d249e0
commit 8976d78390
15 files changed
+84 -167

No files matched your search

+5 -45
View File
@@ -5,67 +5,27 @@ type ControllerSnapshot = {
activeGamepadId: string | null;
};
type ControllerStatusIndicatorOptions = {
durationMs?: number;
setTimeout?: (callback: () => void, delay: number) => ReturnType<typeof setTimeout>;
clearTimeout?: (timer: ReturnType<typeof setTimeout> | number) => void;
};
function getDeviceLabel(device: ControllerDeviceInfo | undefined): string {
if (!device) return 'Controller';
return device.id || `Gamepad ${device.index}`;
}
export function createControllerStatusIndicator(
dom: {
controllerStatusToast: {
textContent: string;
classList: { add: (...entries: string[]) => void; remove: (...entries: string[]) => void };
};
},
options: ControllerStatusIndicatorOptions = {},
) {
const durationMs = options.durationMs ?? 2200;
const scheduleTimeout = options.setTimeout ?? globalThis.setTimeout;
const cancelTimeout =
options.clearTimeout ??
((timer: ReturnType<typeof setTimeout> | number) =>
globalThis.clearTimeout(timer as ReturnType<typeof setTimeout>));
let hideTimeout: ReturnType<typeof setTimeout> | number | null = null;
/** Watches controller snapshots and calls `notify` when a new device appears. */
export function createControllerStatusIndicator(notify: (message: string) => void) {
let previousConnectedIds = new Set<string>();
function show(message: string): void {
if (hideTimeout !== null) {
cancelTimeout(hideTimeout);
hideTimeout = null;
}
dom.controllerStatusToast.textContent = message;
dom.controllerStatusToast.classList.remove('hidden');
hideTimeout = scheduleTimeout(() => {
dom.controllerStatusToast.classList.add('hidden');
dom.controllerStatusToast.textContent = '';
hideTimeout = null;
}, durationMs);
}
function update(snapshot: ControllerSnapshot): void {
const newDevices = snapshot.connectedGamepads.filter(
(device) => !previousConnectedIds.has(device.id),
);
if (newDevices.length > 0) {
const activeDevice = snapshot.connectedGamepads.find(
(device) => device.id === snapshot.activeGamepadId,
);
const announcedDevice =
newDevices.find((device) => device.id === snapshot.activeGamepadId) ??
newDevices[0] ??
activeDevice;
show(`Controller detected: ${getDeviceLabel(announcedDevice)}`);
newDevices.find((device) => device.id === snapshot.activeGamepadId) ?? newDevices[0];
notify(`Controller connected: ${getDeviceLabel(announcedDevice)}`);
}
previousConnectedIds = new Set(snapshot.connectedGamepads.map((device) => device.id));
}
return { show, update };
return { update };
}