Spaces:
Paused
Paused
| import { browser } from "$app/environment"; | |
| import type { WebHaptics } from "web-haptics"; | |
| let instance: WebHaptics | null = null; | |
| let enabled = true; | |
| /** | |
| * Lazily initializes the WebHaptics instance on first use. | |
| * Avoids importing at module level so SSR doesn't break. | |
| */ | |
| async function getInstance(): Promise<WebHaptics | null> { | |
| if (!browser || !supportsHaptics()) return null; | |
| if (instance) return instance; | |
| try { | |
| const { WebHaptics: WH } = await import("web-haptics"); | |
| instance = new WH(); | |
| return instance; | |
| } catch { | |
| return null; | |
| } | |
| } | |
| /** Call from the settings store to keep haptics in sync with user preference. */ | |
| export function setHapticsEnabled(value: boolean) { | |
| enabled = value; | |
| } | |
| /** Whether the device likely supports haptic feedback (touch screen present). */ | |
| export function supportsHaptics(): boolean { | |
| return browser && navigator.maxTouchPoints > 0; | |
| } | |
| // ββ Internals ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| /** Fire a haptic pattern, swallowing errors so callers can safely fire-and-forget. */ | |
| function fire(pattern: string): void { | |
| if (!enabled) return; | |
| Promise.resolve(getInstance()) | |
| .then((h) => h?.trigger(pattern)) | |
| .catch(() => {}); | |
| } | |
| // ββ Semantic haptic actions ββββββββββββββββββββββββββββββββββββββββββ | |
| /** Light tap β for routine actions (send message, toggle, navigate). */ | |
| export function tap() { | |
| fire("light"); | |
| } | |
| /** Success confirmation β double-tap pattern (copy, share, save). */ | |
| export function confirm() { | |
| fire("success"); | |
| } | |
| /** Error / destructive warning β three rapid taps (delete, stop generation). */ | |
| export function error() { | |
| fire("error"); | |
| } | |
| /** Selection change β subtle tap for pickers and selections. */ | |
| export function selection() { | |
| fire("selection"); | |
| } | |
| /** Stream start burst β multiple short vibrations for a "machine starting up" feel. */ | |
| export function streamStart(): void { | |
| if (!enabled || !browser) return; | |
| if (typeof navigator.vibrate !== "function") return; | |
| // Three quick pulses: two short taps + a slightly longer finish | |
| navigator.vibrate([50, 30, 50, 30, 80]); | |
| } | |