-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Expand file tree
/
Copy pathhaptics.ts
More file actions
72 lines (59 loc) · 2.25 KB
/
haptics.ts
File metadata and controls
72 lines (59 loc) · 2.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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]);
}