"use client";
import { settingsStore } from "./settings";
/**
* Best-effort haptics, behind the Settings → Haptics toggle (on by default).
*
* Android/Chrome use the Vibration API. iOS ignores `navigator.vibrate`;
* since iOS 17.4 the system tick can be triggered by clicking a hidden
* `` label — we try that, throttled, and fail
* silently everywhere else. Nothing here ever throws or blocks input.
*/
export type HapticKind = "tap" | "toggle" | "success" | "error" | "celebrate";
const PATTERNS: Record = {
tap: 8,
toggle: 12,
success: [14, 40, 18],
error: [30, 60, 30],
celebrate: [18, 50, 18, 50, 26],
};
let lastIosTick = 0;
function iosTick(): void {
const now = Date.now();
if (now - lastIosTick < 80) return;
lastIosTick = now;
try {
const label = document.createElement("label");
label.setAttribute("aria-hidden", "true");
label.style.position = "fixed";
label.style.left = "-9999px";
const input = document.createElement("input");
input.type = "checkbox";
input.setAttribute("switch", "");
label.appendChild(input);
document.body.appendChild(label);
input.click();
window.setTimeout(() => label.remove(), 0);
} catch {
/* no haptics available */
}
}
export function haptic(kind: HapticKind = "tap"): void {
if (typeof window === "undefined") return;
try {
if (settingsStore.getSnapshot().haptics === false) return;
} catch {
return;
}
try {
if (typeof navigator !== "undefined" && typeof navigator.vibrate === "function") {
navigator.vibrate(PATTERNS[kind]);
return;
}
iosTick();
} catch {
/* silent */
}
}