web-haptics 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -12
- package/dist/chunk-4NSAIXAB.mjs +2 -0
- package/dist/index.d.mts +66 -22
- package/dist/index.d.ts +66 -22
- package/dist/index.js +1 -2
- package/dist/index.mjs +1 -2
- package/dist/react/index.d.mts +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +1 -2
- package/dist/react/index.mjs +1 -2
- package/dist/svelte/index.js +1 -2
- package/dist/svelte/index.mjs +1 -2
- package/dist/{types-DkzcH7AA.d.mts → types-BVHXO1ni.d.mts} +0 -1
- package/dist/{types-DkzcH7AA.d.ts → types-BVHXO1ni.d.ts} +0 -1
- package/dist/vue/index.d.mts +0 -1
- package/dist/vue/index.d.ts +0 -1
- package/dist/vue/index.js +1 -2
- package/dist/vue/index.mjs +1 -2
- package/package.json +5 -2
- package/dist/.DS_Store +0 -0
- package/dist/chunk-4RZ72ZZU.mjs +0 -3
- package/dist/chunk-4RZ72ZZU.mjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/react/index.js.map +0 -1
- package/dist/react/index.mjs.map +0 -1
- package/dist/svelte/index.d.mts +0 -28
- package/dist/svelte/index.d.ts +0 -28
- package/dist/svelte/index.js.map +0 -1
- package/dist/svelte/index.mjs.map +0 -1
- package/dist/vue/index.js.map +0 -1
- package/dist/vue/index.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -59,34 +59,57 @@ haptics.trigger("success");
|
|
|
59
59
|
|
|
60
60
|
## Built-in Presets
|
|
61
61
|
|
|
62
|
-
| Name | Pattern
|
|
63
|
-
| --------- |
|
|
64
|
-
| `success` | `[50, 50, 50]`
|
|
65
|
-
| `nudge` | `[80,
|
|
66
|
-
| `error` | `[
|
|
67
|
-
| `buzz` | `[1000]`
|
|
62
|
+
| Name | Pattern | Description |
|
|
63
|
+
| --------- | ---------------------------------------------------------------- | ------------------------------ |
|
|
64
|
+
| `success` | `[{ duration: 50 }, { delay: 50, duration: 50 }]` | Two taps indicating success |
|
|
65
|
+
| `nudge` | `[{ duration: 80, intensity: 0.8 }, { delay: 80, duration: 50, intensity: 0.3 }]` | Strong tap followed by a soft tap |
|
|
66
|
+
| `error` | `[{ duration: 50, intensity: 0.75 }, ...]` ×3 | Three sharp taps for errors |
|
|
67
|
+
| `buzz` | `[{ duration: 1000, intensity: 1 }]` | A long vibration |
|
|
68
68
|
|
|
69
69
|
You can also pass custom patterns directly:
|
|
70
70
|
|
|
71
71
|
```ts
|
|
72
|
-
trigger([100, 50, 100]); //
|
|
73
|
-
trigger(200); // single vibration
|
|
74
|
-
trigger({
|
|
72
|
+
trigger([100, 50, 100]); // number[] shorthand (alternating on/off durations)
|
|
73
|
+
trigger(200); // single vibration (ms)
|
|
74
|
+
trigger([{ duration: 80, intensity: 0.8 }, { delay: 50, duration: 100 }]); // Vibration[]
|
|
75
|
+
trigger({
|
|
76
|
+
pattern: [{ duration: 50 }, { delay: 50, duration: 50 }],
|
|
77
|
+
description: "custom",
|
|
78
|
+
}); // full HapticPreset
|
|
75
79
|
```
|
|
76
80
|
|
|
77
81
|
## API
|
|
78
82
|
|
|
79
|
-
### `
|
|
83
|
+
### `new WebHaptics(options?)`
|
|
84
|
+
|
|
85
|
+
Create a new instance.
|
|
86
|
+
|
|
87
|
+
- `options.debug` — enable audio feedback for testing on desktop (default `false`)
|
|
88
|
+
- `options.showSwitch` — show the haptic feedback toggle switch (default `false`)
|
|
89
|
+
|
|
90
|
+
### `trigger(input?, options?): Promise<void>`
|
|
80
91
|
|
|
81
92
|
Trigger haptic feedback.
|
|
82
93
|
|
|
83
|
-
- `input` — preset name (`"success"`),
|
|
84
|
-
- `options.intensity` — override intensity (0–1)
|
|
94
|
+
- `input` — preset name (`"success"`), duration in ms, `number[]`, `Vibration[]`, or `HapticPreset`
|
|
95
|
+
- `options.intensity` — override default intensity (0–1, default `0.5`)
|
|
85
96
|
|
|
86
97
|
### `cancel()`
|
|
87
98
|
|
|
88
99
|
Stop the current pattern and cancel any ongoing vibration.
|
|
89
100
|
|
|
101
|
+
### `destroy()`
|
|
102
|
+
|
|
103
|
+
Clean up DOM elements and audio resources. Call when the instance is no longer needed.
|
|
104
|
+
|
|
105
|
+
### `setDebug(debug: boolean)`
|
|
106
|
+
|
|
107
|
+
Enable or disable debug audio feedback.
|
|
108
|
+
|
|
109
|
+
### `setShowSwitch(show: boolean)`
|
|
110
|
+
|
|
111
|
+
Show or hide the haptic feedback toggle switch.
|
|
112
|
+
|
|
90
113
|
### `WebHaptics.isSupported`
|
|
91
114
|
|
|
92
115
|
Static boolean — `true` if the device supports the Vibration API.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
var b={success:{pattern:[{duration:30,intensity:.5},{delay:60,duration:40,intensity:1}]},warning:{pattern:[{duration:40,intensity:.8},{delay:100,duration:40,intensity:.6}]},error:{pattern:[{duration:40,intensity:.9},{delay:40,duration:40,intensity:.9},{delay:40,duration:40,intensity:.9}]},light:{pattern:[{duration:15,intensity:.4}]},medium:{pattern:[{duration:25,intensity:.7}]},heavy:{pattern:[{duration:35,intensity:1}]},soft:{pattern:[{duration:40,intensity:.5}]},rigid:{pattern:[{duration:10,intensity:1}]},selection:{pattern:[{duration:8,intensity:.3}]},nudge:{pattern:[{duration:80,intensity:.8},{delay:80,duration:50,intensity:.3}]},buzz:{pattern:[{duration:1e3,intensity:1}]}};var g=16,x=184,m=1e3,p=20;function C(o){if(typeof o=="number")return{vibrations:[{duration:o}]};if(typeof o=="string"){let i=b[o];return i?{vibrations:i.pattern.map(t=>({...t}))}:(console.warn(`[web-haptics] Unknown preset: "${o}"`),null)}if(Array.isArray(o)){if(o.length===0)return{vibrations:[]};if(typeof o[0]=="number"){let i=o,t=[];for(let e=0;e<i.length;e+=2){let n=e>0?i[e-1]:0;t.push({...n>0&&{delay:n},duration:i[e]})}return{vibrations:t}}return{vibrations:o.map(i=>({...i}))}}return{vibrations:o.pattern.map(i=>({...i}))}}function w(o,i){if(i>=1)return[o];if(i<=0)return[];let t=Math.max(1,Math.round(p*i)),e=p-t,n=[],s=o;for(;s>=p;)n.push(t),n.push(e),s-=p;if(s>0){let a=Math.max(1,Math.round(s*i));n.push(a);let r=s-a;r>0&&n.push(r)}return n}function M(o,i){let t=[];for(let e=0;e<o.length;e++){let n=o[e],s=Math.max(0,Math.min(1,n.intensity??i)),a=n.delay??0;a>0&&(t.length>0&&t.length%2===0?t[t.length-1]+=a:(t.length===0&&t.push(0),t.push(a)));let r=w(n.duration,s);if(r.length===0){t.length>0&&t.length%2===0?t[t.length-1]+=n.duration:n.duration>0&&(t.push(0),t.push(n.duration));continue}for(let d of r)t.push(d)}return t}var I=0,v=class o{hapticLabel=null;domInitialized=!1;instanceId;debug;showSwitch;rafId=null;patternResolve=null;audioCtx=null;audioFilter=null;audioGain=null;audioBuffer=null;constructor(i){this.instanceId=++I,this.debug=i?.debug??!1,this.showSwitch=i?.showSwitch??!1}static isSupported=typeof navigator<"u"&&typeof navigator.vibrate=="function";async trigger(i=[{duration:25,intensity:.7}],t){let e=C(i);if(!e)return;let{vibrations:n}=e;if(n.length===0)return;let s=Math.max(0,Math.min(1,t?.intensity??.5));for(let a of n)if(a.duration>m&&(a.duration=m),!Number.isFinite(a.duration)||a.duration<0||a.delay!==void 0&&(!Number.isFinite(a.delay)||a.delay<0)){console.warn("[web-haptics] Invalid vibration values. Durations and delays must be finite non-negative numbers.");return}if(o.isSupported&&navigator.vibrate(M(n,s)),!o.isSupported||this.debug){if(this.ensureDOM(),!this.hapticLabel)return;this.debug&&await this.ensureAudio(),this.stopPattern();let r=(n[0]?.delay??0)===0;if(r&&(this.hapticLabel.click(),this.debug&&this.audioCtx)){let d=Math.max(0,Math.min(1,n[0].intensity??s));this.playClick(d)}await this.runPattern(n,s,r)}}cancel(){this.stopPattern(),o.isSupported&&navigator.vibrate(0)}destroy(){this.stopPattern(),this.hapticLabel&&(this.hapticLabel.remove(),this.hapticLabel=null,this.domInitialized=!1),this.audioCtx&&(this.audioCtx.close(),this.audioCtx=null,this.audioFilter=null,this.audioGain=null,this.audioBuffer=null)}setDebug(i){this.debug=i,!i&&this.audioCtx&&(this.audioCtx.close(),this.audioCtx=null,this.audioFilter=null,this.audioGain=null,this.audioBuffer=null)}setShowSwitch(i){if(this.showSwitch=i,this.hapticLabel){let t=this.hapticLabel.querySelector("input");this.hapticLabel.style.display=i?"":"none",t&&(t.style.display=i?"":"none")}}stopPattern(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.patternResolve?.(),this.patternResolve=null}runPattern(i,t,e){return new Promise(n=>{this.patternResolve=n;let s=[],a=0;for(let u of i){let c=Math.max(0,Math.min(1,u.intensity??t)),l=u.delay??0;l>0&&(a+=l,s.push({end:a,isOn:!1,intensity:0})),a+=u.duration,s.push({end:a,isOn:!0,intensity:c})}let r=a,d=0,h=-1,y=u=>{d===0&&(d=u);let c=u-d;if(c>=r){this.rafId=null,this.patternResolve=null,n();return}let l=s[0];for(let f of s)if(c<f.end){l=f;break}if(l.isOn){let f=g+(1-l.intensity)*x;h===-1?(h=u,e||(this.hapticLabel?.click(),this.debug&&this.audioCtx&&this.playClick(l.intensity),e=!0)):u-h>=f&&(this.hapticLabel?.click(),this.debug&&this.audioCtx&&this.playClick(l.intensity),h=u)}this.rafId=requestAnimationFrame(y)};this.rafId=requestAnimationFrame(y)})}playClick(i){if(!this.audioCtx||!this.audioFilter||!this.audioGain||!this.audioBuffer)return;let t=this.audioBuffer.getChannelData(0);for(let a=0;a<t.length;a++)t[a]=(Math.random()*2-1)*Math.exp(-a/25);this.audioGain.gain.value=.5*i;let e=2e3+i*2e3,n=1+(Math.random()-.5)*.3;this.audioFilter.frequency.value=e*n;let s=this.audioCtx.createBufferSource();s.buffer=this.audioBuffer,s.connect(this.audioFilter),s.onended=()=>s.disconnect(),s.start()}async ensureAudio(){if(!this.audioCtx&&typeof AudioContext<"u"){this.audioCtx=new AudioContext,this.audioFilter=this.audioCtx.createBiquadFilter(),this.audioFilter.type="bandpass",this.audioFilter.frequency.value=4e3,this.audioFilter.Q.value=8,this.audioGain=this.audioCtx.createGain(),this.audioFilter.connect(this.audioGain),this.audioGain.connect(this.audioCtx.destination);let i=.004;this.audioBuffer=this.audioCtx.createBuffer(1,this.audioCtx.sampleRate*i,this.audioCtx.sampleRate);let t=this.audioBuffer.getChannelData(0);for(let e=0;e<t.length;e++)t[e]=(Math.random()*2-1)*Math.exp(-e/25)}this.audioCtx?.state==="suspended"&&await this.audioCtx.resume()}ensureDOM(){if(this.domInitialized||typeof document>"u")return;let i=`web-haptics-${this.instanceId}`,t=document.createElement("label");t.setAttribute("for",i),t.textContent="Haptic feedback",t.style.position="fixed",t.style.bottom="10px",t.style.left="10px",t.style.padding="5px 10px",t.style.backgroundColor="rgba(0, 0, 0, 0.7)",t.style.color="white",t.style.fontFamily="sans-serif",t.style.fontSize="14px",t.style.borderRadius="4px",t.style.zIndex="9999",t.style.userSelect="none",this.hapticLabel=t;let e=document.createElement("input");e.type="checkbox",e.setAttribute("switch",""),e.id=i,e.style.all="initial",e.style.appearance="auto",this.showSwitch||(t.style.display="none",e.style.display="none"),t.appendChild(e),document.body.appendChild(t),this.domInitialized=!0}};export{b as a,v as b};
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { W as WebHapticsOptions, H as HapticInput, T as TriggerOptions } from './types-
|
|
2
|
-
export { a as HapticPattern, b as HapticPreset, V as Vibration } from './types-
|
|
1
|
+
import { W as WebHapticsOptions, H as HapticInput, T as TriggerOptions } from './types-BVHXO1ni.mjs';
|
|
2
|
+
export { a as HapticPattern, b as HapticPreset, V as Vibration } from './types-BVHXO1ni.mjs';
|
|
3
3
|
|
|
4
|
-
var version = "0.0.
|
|
4
|
+
var version = "0.0.6";
|
|
5
5
|
|
|
6
6
|
declare class WebHaptics {
|
|
7
7
|
private hapticLabel;
|
|
@@ -32,45 +32,89 @@ declare class WebHaptics {
|
|
|
32
32
|
declare const defaultPatterns: {
|
|
33
33
|
readonly success: {
|
|
34
34
|
readonly pattern: [{
|
|
35
|
-
readonly duration:
|
|
35
|
+
readonly duration: 30;
|
|
36
|
+
readonly intensity: 0.5;
|
|
36
37
|
}, {
|
|
37
|
-
readonly delay:
|
|
38
|
-
readonly duration:
|
|
38
|
+
readonly delay: 60;
|
|
39
|
+
readonly duration: 40;
|
|
40
|
+
readonly intensity: 1;
|
|
39
41
|
}];
|
|
40
|
-
readonly description: "A series of taps indicating success.";
|
|
41
42
|
};
|
|
42
|
-
readonly
|
|
43
|
+
readonly warning: {
|
|
43
44
|
readonly pattern: [{
|
|
44
|
-
readonly duration:
|
|
45
|
+
readonly duration: 40;
|
|
45
46
|
readonly intensity: 0.8;
|
|
46
47
|
}, {
|
|
47
|
-
readonly delay:
|
|
48
|
-
readonly duration:
|
|
49
|
-
readonly intensity: 0.
|
|
48
|
+
readonly delay: 100;
|
|
49
|
+
readonly duration: 40;
|
|
50
|
+
readonly intensity: 0.6;
|
|
50
51
|
}];
|
|
51
|
-
readonly description: "A series of taps indicating a nudge.";
|
|
52
52
|
};
|
|
53
53
|
readonly error: {
|
|
54
54
|
readonly pattern: [{
|
|
55
|
-
readonly duration:
|
|
56
|
-
readonly intensity: 0.
|
|
55
|
+
readonly duration: 40;
|
|
56
|
+
readonly intensity: 0.9;
|
|
57
57
|
}, {
|
|
58
|
-
readonly delay:
|
|
59
|
-
readonly duration:
|
|
60
|
-
readonly intensity: 0.
|
|
58
|
+
readonly delay: 40;
|
|
59
|
+
readonly duration: 40;
|
|
60
|
+
readonly intensity: 0.9;
|
|
61
|
+
}, {
|
|
62
|
+
readonly delay: 40;
|
|
63
|
+
readonly duration: 40;
|
|
64
|
+
readonly intensity: 0.9;
|
|
65
|
+
}];
|
|
66
|
+
};
|
|
67
|
+
readonly light: {
|
|
68
|
+
readonly pattern: [{
|
|
69
|
+
readonly duration: 15;
|
|
70
|
+
readonly intensity: 0.4;
|
|
71
|
+
}];
|
|
72
|
+
};
|
|
73
|
+
readonly medium: {
|
|
74
|
+
readonly pattern: [{
|
|
75
|
+
readonly duration: 25;
|
|
76
|
+
readonly intensity: 0.7;
|
|
77
|
+
}];
|
|
78
|
+
};
|
|
79
|
+
readonly heavy: {
|
|
80
|
+
readonly pattern: [{
|
|
81
|
+
readonly duration: 35;
|
|
82
|
+
readonly intensity: 1;
|
|
83
|
+
}];
|
|
84
|
+
};
|
|
85
|
+
readonly soft: {
|
|
86
|
+
readonly pattern: [{
|
|
87
|
+
readonly duration: 40;
|
|
88
|
+
readonly intensity: 0.5;
|
|
89
|
+
}];
|
|
90
|
+
};
|
|
91
|
+
readonly rigid: {
|
|
92
|
+
readonly pattern: [{
|
|
93
|
+
readonly duration: 10;
|
|
94
|
+
readonly intensity: 1;
|
|
95
|
+
}];
|
|
96
|
+
};
|
|
97
|
+
readonly selection: {
|
|
98
|
+
readonly pattern: [{
|
|
99
|
+
readonly duration: 8;
|
|
100
|
+
readonly intensity: 0.3;
|
|
101
|
+
}];
|
|
102
|
+
};
|
|
103
|
+
readonly nudge: {
|
|
104
|
+
readonly pattern: [{
|
|
105
|
+
readonly duration: 80;
|
|
106
|
+
readonly intensity: 0.8;
|
|
61
107
|
}, {
|
|
62
|
-
readonly delay:
|
|
108
|
+
readonly delay: 80;
|
|
63
109
|
readonly duration: 50;
|
|
64
|
-
readonly intensity: 0.
|
|
110
|
+
readonly intensity: 0.3;
|
|
65
111
|
}];
|
|
66
|
-
readonly description: "A series of taps indicating a warning or error.";
|
|
67
112
|
};
|
|
68
113
|
readonly buzz: {
|
|
69
114
|
readonly pattern: [{
|
|
70
115
|
readonly duration: 1000;
|
|
71
116
|
readonly intensity: 1;
|
|
72
117
|
}];
|
|
73
|
-
readonly description: "A long vibration.";
|
|
74
118
|
};
|
|
75
119
|
};
|
|
76
120
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { W as WebHapticsOptions, H as HapticInput, T as TriggerOptions } from './types-
|
|
2
|
-
export { a as HapticPattern, b as HapticPreset, V as Vibration } from './types-
|
|
1
|
+
import { W as WebHapticsOptions, H as HapticInput, T as TriggerOptions } from './types-BVHXO1ni.js';
|
|
2
|
+
export { a as HapticPattern, b as HapticPreset, V as Vibration } from './types-BVHXO1ni.js';
|
|
3
3
|
|
|
4
|
-
var version = "0.0.
|
|
4
|
+
var version = "0.0.6";
|
|
5
5
|
|
|
6
6
|
declare class WebHaptics {
|
|
7
7
|
private hapticLabel;
|
|
@@ -32,45 +32,89 @@ declare class WebHaptics {
|
|
|
32
32
|
declare const defaultPatterns: {
|
|
33
33
|
readonly success: {
|
|
34
34
|
readonly pattern: [{
|
|
35
|
-
readonly duration:
|
|
35
|
+
readonly duration: 30;
|
|
36
|
+
readonly intensity: 0.5;
|
|
36
37
|
}, {
|
|
37
|
-
readonly delay:
|
|
38
|
-
readonly duration:
|
|
38
|
+
readonly delay: 60;
|
|
39
|
+
readonly duration: 40;
|
|
40
|
+
readonly intensity: 1;
|
|
39
41
|
}];
|
|
40
|
-
readonly description: "A series of taps indicating success.";
|
|
41
42
|
};
|
|
42
|
-
readonly
|
|
43
|
+
readonly warning: {
|
|
43
44
|
readonly pattern: [{
|
|
44
|
-
readonly duration:
|
|
45
|
+
readonly duration: 40;
|
|
45
46
|
readonly intensity: 0.8;
|
|
46
47
|
}, {
|
|
47
|
-
readonly delay:
|
|
48
|
-
readonly duration:
|
|
49
|
-
readonly intensity: 0.
|
|
48
|
+
readonly delay: 100;
|
|
49
|
+
readonly duration: 40;
|
|
50
|
+
readonly intensity: 0.6;
|
|
50
51
|
}];
|
|
51
|
-
readonly description: "A series of taps indicating a nudge.";
|
|
52
52
|
};
|
|
53
53
|
readonly error: {
|
|
54
54
|
readonly pattern: [{
|
|
55
|
-
readonly duration:
|
|
56
|
-
readonly intensity: 0.
|
|
55
|
+
readonly duration: 40;
|
|
56
|
+
readonly intensity: 0.9;
|
|
57
57
|
}, {
|
|
58
|
-
readonly delay:
|
|
59
|
-
readonly duration:
|
|
60
|
-
readonly intensity: 0.
|
|
58
|
+
readonly delay: 40;
|
|
59
|
+
readonly duration: 40;
|
|
60
|
+
readonly intensity: 0.9;
|
|
61
|
+
}, {
|
|
62
|
+
readonly delay: 40;
|
|
63
|
+
readonly duration: 40;
|
|
64
|
+
readonly intensity: 0.9;
|
|
65
|
+
}];
|
|
66
|
+
};
|
|
67
|
+
readonly light: {
|
|
68
|
+
readonly pattern: [{
|
|
69
|
+
readonly duration: 15;
|
|
70
|
+
readonly intensity: 0.4;
|
|
71
|
+
}];
|
|
72
|
+
};
|
|
73
|
+
readonly medium: {
|
|
74
|
+
readonly pattern: [{
|
|
75
|
+
readonly duration: 25;
|
|
76
|
+
readonly intensity: 0.7;
|
|
77
|
+
}];
|
|
78
|
+
};
|
|
79
|
+
readonly heavy: {
|
|
80
|
+
readonly pattern: [{
|
|
81
|
+
readonly duration: 35;
|
|
82
|
+
readonly intensity: 1;
|
|
83
|
+
}];
|
|
84
|
+
};
|
|
85
|
+
readonly soft: {
|
|
86
|
+
readonly pattern: [{
|
|
87
|
+
readonly duration: 40;
|
|
88
|
+
readonly intensity: 0.5;
|
|
89
|
+
}];
|
|
90
|
+
};
|
|
91
|
+
readonly rigid: {
|
|
92
|
+
readonly pattern: [{
|
|
93
|
+
readonly duration: 10;
|
|
94
|
+
readonly intensity: 1;
|
|
95
|
+
}];
|
|
96
|
+
};
|
|
97
|
+
readonly selection: {
|
|
98
|
+
readonly pattern: [{
|
|
99
|
+
readonly duration: 8;
|
|
100
|
+
readonly intensity: 0.3;
|
|
101
|
+
}];
|
|
102
|
+
};
|
|
103
|
+
readonly nudge: {
|
|
104
|
+
readonly pattern: [{
|
|
105
|
+
readonly duration: 80;
|
|
106
|
+
readonly intensity: 0.8;
|
|
61
107
|
}, {
|
|
62
|
-
readonly delay:
|
|
108
|
+
readonly delay: 80;
|
|
63
109
|
readonly duration: 50;
|
|
64
|
-
readonly intensity: 0.
|
|
110
|
+
readonly intensity: 0.3;
|
|
65
111
|
}];
|
|
66
|
-
readonly description: "A series of taps indicating a warning or error.";
|
|
67
112
|
};
|
|
68
113
|
readonly buzz: {
|
|
69
114
|
readonly pattern: [{
|
|
70
115
|
readonly duration: 1000;
|
|
71
116
|
readonly intensity: 1;
|
|
72
117
|
}];
|
|
73
|
-
readonly description: "A long vibration.";
|
|
74
118
|
};
|
|
75
119
|
};
|
|
76
120
|
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
"use strict";var
|
|
3
|
-
//# sourceMappingURL=index.js.map
|
|
2
|
+
"use strict";var m=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var M=Object.prototype.hasOwnProperty;var I=(s,i)=>{for(var t in i)m(s,t,{get:i[t],enumerable:!0})},P=(s,i,t,e)=>{if(i&&typeof i=="object"||typeof i=="function")for(let n of w(i))!M.call(s,n)&&n!==t&&m(s,n,{get:()=>i[n],enumerable:!(e=C(i,n))||e.enumerable});return s};var k=s=>P(m({},"__esModule",{value:!0}),s);var G={};I(G,{WebHaptics:()=>y,defaultPatterns:()=>f,version:()=>g});module.exports=k(G);var g="0.0.6";var f={success:{pattern:[{duration:30,intensity:.5},{delay:60,duration:40,intensity:1}]},warning:{pattern:[{duration:40,intensity:.8},{delay:100,duration:40,intensity:.6}]},error:{pattern:[{duration:40,intensity:.9},{delay:40,duration:40,intensity:.9},{delay:40,duration:40,intensity:.9}]},light:{pattern:[{duration:15,intensity:.4}]},medium:{pattern:[{duration:25,intensity:.7}]},heavy:{pattern:[{duration:35,intensity:1}]},soft:{pattern:[{duration:40,intensity:.5}]},rigid:{pattern:[{duration:10,intensity:1}]},selection:{pattern:[{duration:8,intensity:.3}]},nudge:{pattern:[{duration:80,intensity:.8},{delay:80,duration:50,intensity:.3}]},buzz:{pattern:[{duration:1e3,intensity:1}]}};var L=16,A=184,x=1e3,b=20;function S(s){if(typeof s=="number")return{vibrations:[{duration:s}]};if(typeof s=="string"){let i=f[s];return i?{vibrations:i.pattern.map(t=>({...t}))}:(console.warn(`[web-haptics] Unknown preset: "${s}"`),null)}if(Array.isArray(s)){if(s.length===0)return{vibrations:[]};if(typeof s[0]=="number"){let i=s,t=[];for(let e=0;e<i.length;e+=2){let n=e>0?i[e-1]:0;t.push({...n>0&&{delay:n},duration:i[e]})}return{vibrations:t}}return{vibrations:s.map(i=>({...i}))}}return{vibrations:s.pattern.map(i=>({...i}))}}function O(s,i){if(i>=1)return[s];if(i<=0)return[];let t=Math.max(1,Math.round(b*i)),e=b-t,n=[],r=s;for(;r>=b;)n.push(t),n.push(e),r-=b;if(r>0){let a=Math.max(1,Math.round(r*i));n.push(a);let o=r-a;o>0&&n.push(o)}return n}function H(s,i){let t=[];for(let e=0;e<s.length;e++){let n=s[e],r=Math.max(0,Math.min(1,n.intensity??i)),a=n.delay??0;a>0&&(t.length>0&&t.length%2===0?t[t.length-1]+=a:(t.length===0&&t.push(0),t.push(a)));let o=O(n.duration,r);if(o.length===0){t.length>0&&t.length%2===0?t[t.length-1]+=n.duration:n.duration>0&&(t.push(0),t.push(n.duration));continue}for(let d of o)t.push(d)}return t}var j=0,y=class s{hapticLabel=null;domInitialized=!1;instanceId;debug;showSwitch;rafId=null;patternResolve=null;audioCtx=null;audioFilter=null;audioGain=null;audioBuffer=null;constructor(i){this.instanceId=++j,this.debug=i?.debug??!1,this.showSwitch=i?.showSwitch??!1}static isSupported=typeof navigator<"u"&&typeof navigator.vibrate=="function";async trigger(i=[{duration:25,intensity:.7}],t){let e=S(i);if(!e)return;let{vibrations:n}=e;if(n.length===0)return;let r=Math.max(0,Math.min(1,t?.intensity??.5));for(let a of n)if(a.duration>x&&(a.duration=x),!Number.isFinite(a.duration)||a.duration<0||a.delay!==void 0&&(!Number.isFinite(a.delay)||a.delay<0)){console.warn("[web-haptics] Invalid vibration values. Durations and delays must be finite non-negative numbers.");return}if(s.isSupported&&navigator.vibrate(H(n,r)),!s.isSupported||this.debug){if(this.ensureDOM(),!this.hapticLabel)return;this.debug&&await this.ensureAudio(),this.stopPattern();let o=(n[0]?.delay??0)===0;if(o&&(this.hapticLabel.click(),this.debug&&this.audioCtx)){let d=Math.max(0,Math.min(1,n[0].intensity??r));this.playClick(d)}await this.runPattern(n,r,o)}}cancel(){this.stopPattern(),s.isSupported&&navigator.vibrate(0)}destroy(){this.stopPattern(),this.hapticLabel&&(this.hapticLabel.remove(),this.hapticLabel=null,this.domInitialized=!1),this.audioCtx&&(this.audioCtx.close(),this.audioCtx=null,this.audioFilter=null,this.audioGain=null,this.audioBuffer=null)}setDebug(i){this.debug=i,!i&&this.audioCtx&&(this.audioCtx.close(),this.audioCtx=null,this.audioFilter=null,this.audioGain=null,this.audioBuffer=null)}setShowSwitch(i){if(this.showSwitch=i,this.hapticLabel){let t=this.hapticLabel.querySelector("input");this.hapticLabel.style.display=i?"":"none",t&&(t.style.display=i?"":"none")}}stopPattern(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.patternResolve?.(),this.patternResolve=null}runPattern(i,t,e){return new Promise(n=>{this.patternResolve=n;let r=[],a=0;for(let l of i){let c=Math.max(0,Math.min(1,l.intensity??t)),u=l.delay??0;u>0&&(a+=u,r.push({end:a,isOn:!1,intensity:0})),a+=l.duration,r.push({end:a,isOn:!0,intensity:c})}let o=a,d=0,p=-1,v=l=>{d===0&&(d=l);let c=l-d;if(c>=o){this.rafId=null,this.patternResolve=null,n();return}let u=r[0];for(let h of r)if(c<h.end){u=h;break}if(u.isOn){let h=L+(1-u.intensity)*A;p===-1?(p=l,e||(this.hapticLabel?.click(),this.debug&&this.audioCtx&&this.playClick(u.intensity),e=!0)):l-p>=h&&(this.hapticLabel?.click(),this.debug&&this.audioCtx&&this.playClick(u.intensity),p=l)}this.rafId=requestAnimationFrame(v)};this.rafId=requestAnimationFrame(v)})}playClick(i){if(!this.audioCtx||!this.audioFilter||!this.audioGain||!this.audioBuffer)return;let t=this.audioBuffer.getChannelData(0);for(let a=0;a<t.length;a++)t[a]=(Math.random()*2-1)*Math.exp(-a/25);this.audioGain.gain.value=.5*i;let e=2e3+i*2e3,n=1+(Math.random()-.5)*.3;this.audioFilter.frequency.value=e*n;let r=this.audioCtx.createBufferSource();r.buffer=this.audioBuffer,r.connect(this.audioFilter),r.onended=()=>r.disconnect(),r.start()}async ensureAudio(){if(!this.audioCtx&&typeof AudioContext<"u"){this.audioCtx=new AudioContext,this.audioFilter=this.audioCtx.createBiquadFilter(),this.audioFilter.type="bandpass",this.audioFilter.frequency.value=4e3,this.audioFilter.Q.value=8,this.audioGain=this.audioCtx.createGain(),this.audioFilter.connect(this.audioGain),this.audioGain.connect(this.audioCtx.destination);let i=.004;this.audioBuffer=this.audioCtx.createBuffer(1,this.audioCtx.sampleRate*i,this.audioCtx.sampleRate);let t=this.audioBuffer.getChannelData(0);for(let e=0;e<t.length;e++)t[e]=(Math.random()*2-1)*Math.exp(-e/25)}this.audioCtx?.state==="suspended"&&await this.audioCtx.resume()}ensureDOM(){if(this.domInitialized||typeof document>"u")return;let i=`web-haptics-${this.instanceId}`,t=document.createElement("label");t.setAttribute("for",i),t.textContent="Haptic feedback",t.style.position="fixed",t.style.bottom="10px",t.style.left="10px",t.style.padding="5px 10px",t.style.backgroundColor="rgba(0, 0, 0, 0.7)",t.style.color="white",t.style.fontFamily="sans-serif",t.style.fontSize="14px",t.style.borderRadius="4px",t.style.zIndex="9999",t.style.userSelect="none",this.hapticLabel=t;let e=document.createElement("input");e.type="checkbox",e.setAttribute("switch",""),e.id=i,e.style.all="initial",e.style.appearance="auto",this.showSwitch||(t.style.display="none",e.style.display="none"),t.appendChild(e),document.body.appendChild(t),this.domInitialized=!0}};0&&(module.exports={WebHaptics,defaultPatterns,version});
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import{a as e,b as t}from"./chunk-
|
|
3
|
-
//# sourceMappingURL=index.mjs.map
|
|
2
|
+
import{a as e,b as t}from"./chunk-4NSAIXAB.mjs";var s="0.0.6";export{t as WebHaptics,e as defaultPatterns,s as version};
|
package/dist/react/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { W as WebHapticsOptions, H as HapticInput, T as TriggerOptions } from '../types-
|
|
1
|
+
import { W as WebHapticsOptions, H as HapticInput, T as TriggerOptions } from '../types-BVHXO1ni.mjs';
|
|
2
2
|
|
|
3
3
|
declare function useWebHaptics(options?: WebHapticsOptions): {
|
|
4
4
|
trigger: (input?: HapticInput, options?: TriggerOptions) => Promise<void> | undefined;
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { W as WebHapticsOptions, H as HapticInput, T as TriggerOptions } from '../types-
|
|
1
|
+
import { W as WebHapticsOptions, H as HapticInput, T as TriggerOptions } from '../types-BVHXO1ni.js';
|
|
2
2
|
|
|
3
3
|
declare function useWebHaptics(options?: WebHapticsOptions): {
|
|
4
4
|
trigger: (input?: HapticInput, options?: TriggerOptions) => Promise<void> | undefined;
|
package/dist/react/index.js
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
"use strict";var
|
|
3
|
-
//# sourceMappingURL=index.js.map
|
|
2
|
+
"use strict";var m=Object.defineProperty;var w=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var M=Object.prototype.hasOwnProperty;var S=(n,i)=>{for(var t in i)m(n,t,{get:i[t],enumerable:!0})},F=(n,i,t,e)=>{if(i&&typeof i=="object"||typeof i=="function")for(let a of I(i))!M.call(n,a)&&a!==t&&m(n,a,{get:()=>i[a],enumerable:!(e=w(i,a))||e.enumerable});return n};var L=n=>F(m({},"__esModule",{value:!0}),n);var B={};S(B,{useWebHaptics:()=>C});module.exports=L(B);var l=require("react");var v={success:{pattern:[{duration:30,intensity:.5},{delay:60,duration:40,intensity:1}]},warning:{pattern:[{duration:40,intensity:.8},{delay:100,duration:40,intensity:.6}]},error:{pattern:[{duration:40,intensity:.9},{delay:40,duration:40,intensity:.9},{delay:40,duration:40,intensity:.9}]},light:{pattern:[{duration:15,intensity:.4}]},medium:{pattern:[{duration:25,intensity:.7}]},heavy:{pattern:[{duration:35,intensity:1}]},soft:{pattern:[{duration:40,intensity:.5}]},rigid:{pattern:[{duration:10,intensity:1}]},selection:{pattern:[{duration:8,intensity:.3}]},nudge:{pattern:[{duration:80,intensity:.8},{delay:80,duration:50,intensity:.3}]},buzz:{pattern:[{duration:1e3,intensity:1}]}};var O=16,P=184,x=1e3,y=20;function H(n){if(typeof n=="number")return{vibrations:[{duration:n}]};if(typeof n=="string"){let i=v[n];return i?{vibrations:i.pattern.map(t=>({...t}))}:(console.warn(`[web-haptics] Unknown preset: "${n}"`),null)}if(Array.isArray(n)){if(n.length===0)return{vibrations:[]};if(typeof n[0]=="number"){let i=n,t=[];for(let e=0;e<i.length;e+=2){let a=e>0?i[e-1]:0;t.push({...a>0&&{delay:a},duration:i[e]})}return{vibrations:t}}return{vibrations:n.map(i=>({...i}))}}return{vibrations:n.pattern.map(i=>({...i}))}}function k(n,i){if(i>=1)return[n];if(i<=0)return[];let t=Math.max(1,Math.round(y*i)),e=y-t,a=[],r=n;for(;r>=y;)a.push(t),a.push(e),r-=y;if(r>0){let s=Math.max(1,Math.round(r*i));a.push(s);let o=r-s;o>0&&a.push(o)}return a}function A(n,i){let t=[];for(let e=0;e<n.length;e++){let a=n[e],r=Math.max(0,Math.min(1,a.intensity??i)),s=a.delay??0;s>0&&(t.length>0&&t.length%2===0?t[t.length-1]+=s:(t.length===0&&t.push(0),t.push(s)));let o=k(a.duration,r);if(o.length===0){t.length>0&&t.length%2===0?t[t.length-1]+=a.duration:a.duration>0&&(t.push(0),t.push(a.duration));continue}for(let c of o)t.push(c)}return t}var G=0,h=class n{hapticLabel=null;domInitialized=!1;instanceId;debug;showSwitch;rafId=null;patternResolve=null;audioCtx=null;audioFilter=null;audioGain=null;audioBuffer=null;constructor(i){this.instanceId=++G,this.debug=i?.debug??!1,this.showSwitch=i?.showSwitch??!1}static isSupported=typeof navigator<"u"&&typeof navigator.vibrate=="function";async trigger(i=[{duration:25,intensity:.7}],t){let e=H(i);if(!e)return;let{vibrations:a}=e;if(a.length===0)return;let r=Math.max(0,Math.min(1,t?.intensity??.5));for(let s of a)if(s.duration>x&&(s.duration=x),!Number.isFinite(s.duration)||s.duration<0||s.delay!==void 0&&(!Number.isFinite(s.delay)||s.delay<0)){console.warn("[web-haptics] Invalid vibration values. Durations and delays must be finite non-negative numbers.");return}if(n.isSupported&&navigator.vibrate(A(a,r)),!n.isSupported||this.debug){if(this.ensureDOM(),!this.hapticLabel)return;this.debug&&await this.ensureAudio(),this.stopPattern();let o=(a[0]?.delay??0)===0;if(o&&(this.hapticLabel.click(),this.debug&&this.audioCtx)){let c=Math.max(0,Math.min(1,a[0].intensity??r));this.playClick(c)}await this.runPattern(a,r,o)}}cancel(){this.stopPattern(),n.isSupported&&navigator.vibrate(0)}destroy(){this.stopPattern(),this.hapticLabel&&(this.hapticLabel.remove(),this.hapticLabel=null,this.domInitialized=!1),this.audioCtx&&(this.audioCtx.close(),this.audioCtx=null,this.audioFilter=null,this.audioGain=null,this.audioBuffer=null)}setDebug(i){this.debug=i,!i&&this.audioCtx&&(this.audioCtx.close(),this.audioCtx=null,this.audioFilter=null,this.audioGain=null,this.audioBuffer=null)}setShowSwitch(i){if(this.showSwitch=i,this.hapticLabel){let t=this.hapticLabel.querySelector("input");this.hapticLabel.style.display=i?"":"none",t&&(t.style.display=i?"":"none")}}stopPattern(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.patternResolve?.(),this.patternResolve=null}runPattern(i,t,e){return new Promise(a=>{this.patternResolve=a;let r=[],s=0;for(let u of i){let f=Math.max(0,Math.min(1,u.intensity??t)),d=u.delay??0;d>0&&(s+=d,r.push({end:s,isOn:!1,intensity:0})),s+=u.duration,r.push({end:s,isOn:!0,intensity:f})}let o=s,c=0,p=-1,g=u=>{c===0&&(c=u);let f=u-c;if(f>=o){this.rafId=null,this.patternResolve=null,a();return}let d=r[0];for(let b of r)if(f<b.end){d=b;break}if(d.isOn){let b=O+(1-d.intensity)*P;p===-1?(p=u,e||(this.hapticLabel?.click(),this.debug&&this.audioCtx&&this.playClick(d.intensity),e=!0)):u-p>=b&&(this.hapticLabel?.click(),this.debug&&this.audioCtx&&this.playClick(d.intensity),p=u)}this.rafId=requestAnimationFrame(g)};this.rafId=requestAnimationFrame(g)})}playClick(i){if(!this.audioCtx||!this.audioFilter||!this.audioGain||!this.audioBuffer)return;let t=this.audioBuffer.getChannelData(0);for(let s=0;s<t.length;s++)t[s]=(Math.random()*2-1)*Math.exp(-s/25);this.audioGain.gain.value=.5*i;let e=2e3+i*2e3,a=1+(Math.random()-.5)*.3;this.audioFilter.frequency.value=e*a;let r=this.audioCtx.createBufferSource();r.buffer=this.audioBuffer,r.connect(this.audioFilter),r.onended=()=>r.disconnect(),r.start()}async ensureAudio(){if(!this.audioCtx&&typeof AudioContext<"u"){this.audioCtx=new AudioContext,this.audioFilter=this.audioCtx.createBiquadFilter(),this.audioFilter.type="bandpass",this.audioFilter.frequency.value=4e3,this.audioFilter.Q.value=8,this.audioGain=this.audioCtx.createGain(),this.audioFilter.connect(this.audioGain),this.audioGain.connect(this.audioCtx.destination);let i=.004;this.audioBuffer=this.audioCtx.createBuffer(1,this.audioCtx.sampleRate*i,this.audioCtx.sampleRate);let t=this.audioBuffer.getChannelData(0);for(let e=0;e<t.length;e++)t[e]=(Math.random()*2-1)*Math.exp(-e/25)}this.audioCtx?.state==="suspended"&&await this.audioCtx.resume()}ensureDOM(){if(this.domInitialized||typeof document>"u")return;let i=`web-haptics-${this.instanceId}`,t=document.createElement("label");t.setAttribute("for",i),t.textContent="Haptic feedback",t.style.position="fixed",t.style.bottom="10px",t.style.left="10px",t.style.padding="5px 10px",t.style.backgroundColor="rgba(0, 0, 0, 0.7)",t.style.color="white",t.style.fontFamily="sans-serif",t.style.fontSize="14px",t.style.borderRadius="4px",t.style.zIndex="9999",t.style.userSelect="none",this.hapticLabel=t;let e=document.createElement("input");e.type="checkbox",e.setAttribute("switch",""),e.id=i,e.style.all="initial",e.style.appearance="auto",this.showSwitch||(t.style.display="none",e.style.display="none"),t.appendChild(e),document.body.appendChild(t),this.domInitialized=!0}};function C(n){let i=(0,l.useRef)(null);(0,l.useEffect)(()=>(i.current=new h(n),()=>{i.current?.destroy(),i.current=null}),[]),(0,l.useEffect)(()=>{i.current?.setDebug(n?.debug??!1)},[n?.debug]),(0,l.useEffect)(()=>{i.current?.setShowSwitch(n?.showSwitch??!1)},[n?.showSwitch]);let t=(0,l.useCallback)((r,s)=>i.current?.trigger(r,s),[]),e=(0,l.useCallback)(()=>i.current?.cancel(),[]),a=h.isSupported;return{trigger:t,cancel:e,isSupported:a}}0&&(module.exports={useWebHaptics});
|
package/dist/react/index.mjs
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import{b as r}from"../chunk-
|
|
3
|
-
//# sourceMappingURL=index.mjs.map
|
|
2
|
+
import{b as r}from"../chunk-4NSAIXAB.mjs";import{useRef as o,useEffect as c,useCallback as s}from"react";function l(t){let e=o(null);c(()=>(e.current=new r(t),()=>{e.current?.destroy(),e.current=null}),[]),c(()=>{e.current?.setDebug(t?.debug??!1)},[t?.debug]),c(()=>{e.current?.setShowSwitch(t?.showSwitch??!1)},[t?.showSwitch]);let n=s((p,a)=>e.current?.trigger(p,a),[]),u=s(()=>e.current?.cancel(),[]),i=r.isSupported;return{trigger:n,cancel:u,isSupported:i}}export{l as useWebHaptics};
|
package/dist/svelte/index.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
"use strict";var y=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var M=(a,i)=>{for(var t in i)y(a,t,{get:i[t],enumerable:!0})},
|
|
2
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
"use strict";var y=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var M=(a,i)=>{for(var t in i)y(a,t,{get:i[t],enumerable:!0})},F=(a,i,t,e)=>{if(i&&typeof i=="object"||typeof i=="function")for(let n of w(i))!I.call(a,n)&&n!==t&&y(a,n,{get:()=>i[n],enumerable:!(e=C(i,n))||e.enumerable});return a};var S=a=>F(y({},"__esModule",{value:!0}),a);var G={};M(G,{createWebHaptics:()=>x});module.exports=S(G);var g={success:{pattern:[{duration:30,intensity:.5},{delay:60,duration:40,intensity:1}]},warning:{pattern:[{duration:40,intensity:.8},{delay:100,duration:40,intensity:.6}]},error:{pattern:[{duration:40,intensity:.9},{delay:40,duration:40,intensity:.9},{delay:40,duration:40,intensity:.9}]},light:{pattern:[{duration:15,intensity:.4}]},medium:{pattern:[{duration:25,intensity:.7}]},heavy:{pattern:[{duration:35,intensity:1}]},soft:{pattern:[{duration:40,intensity:.5}]},rigid:{pattern:[{duration:10,intensity:1}]},selection:{pattern:[{duration:8,intensity:.3}]},nudge:{pattern:[{duration:80,intensity:.8},{delay:80,duration:50,intensity:.3}]},buzz:{pattern:[{duration:1e3,intensity:1}]}};var L=16,O=184,v=1e3,b=20;function P(a){if(typeof a=="number")return{vibrations:[{duration:a}]};if(typeof a=="string"){let i=g[a];return i?{vibrations:i.pattern.map(t=>({...t}))}:(console.warn(`[web-haptics] Unknown preset: "${a}"`),null)}if(Array.isArray(a)){if(a.length===0)return{vibrations:[]};if(typeof a[0]=="number"){let i=a,t=[];for(let e=0;e<i.length;e+=2){let n=e>0?i[e-1]:0;t.push({...n>0&&{delay:n},duration:i[e]})}return{vibrations:t}}return{vibrations:a.map(i=>({...i}))}}return{vibrations:a.pattern.map(i=>({...i}))}}function A(a,i){if(i>=1)return[a];if(i<=0)return[];let t=Math.max(1,Math.round(b*i)),e=b-t,n=[],o=a;for(;o>=b;)n.push(t),n.push(e),o-=b;if(o>0){let s=Math.max(1,Math.round(o*i));n.push(s);let r=o-s;r>0&&n.push(r)}return n}function H(a,i){let t=[];for(let e=0;e<a.length;e++){let n=a[e],o=Math.max(0,Math.min(1,n.intensity??i)),s=n.delay??0;s>0&&(t.length>0&&t.length%2===0?t[t.length-1]+=s:(t.length===0&&t.push(0),t.push(s)));let r=A(n.duration,o);if(r.length===0){t.length>0&&t.length%2===0?t[t.length-1]+=n.duration:n.duration>0&&(t.push(0),t.push(n.duration));continue}for(let u of r)t.push(u)}return t}var k=0,h=class a{hapticLabel=null;domInitialized=!1;instanceId;debug;showSwitch;rafId=null;patternResolve=null;audioCtx=null;audioFilter=null;audioGain=null;audioBuffer=null;constructor(i){this.instanceId=++k,this.debug=i?.debug??!1,this.showSwitch=i?.showSwitch??!1}static isSupported=typeof navigator<"u"&&typeof navigator.vibrate=="function";async trigger(i=[{duration:25,intensity:.7}],t){let e=P(i);if(!e)return;let{vibrations:n}=e;if(n.length===0)return;let o=Math.max(0,Math.min(1,t?.intensity??.5));for(let s of n)if(s.duration>v&&(s.duration=v),!Number.isFinite(s.duration)||s.duration<0||s.delay!==void 0&&(!Number.isFinite(s.delay)||s.delay<0)){console.warn("[web-haptics] Invalid vibration values. Durations and delays must be finite non-negative numbers.");return}if(a.isSupported&&navigator.vibrate(H(n,o)),!a.isSupported||this.debug){if(this.ensureDOM(),!this.hapticLabel)return;this.debug&&await this.ensureAudio(),this.stopPattern();let r=(n[0]?.delay??0)===0;if(r&&(this.hapticLabel.click(),this.debug&&this.audioCtx)){let u=Math.max(0,Math.min(1,n[0].intensity??o));this.playClick(u)}await this.runPattern(n,o,r)}}cancel(){this.stopPattern(),a.isSupported&&navigator.vibrate(0)}destroy(){this.stopPattern(),this.hapticLabel&&(this.hapticLabel.remove(),this.hapticLabel=null,this.domInitialized=!1),this.audioCtx&&(this.audioCtx.close(),this.audioCtx=null,this.audioFilter=null,this.audioGain=null,this.audioBuffer=null)}setDebug(i){this.debug=i,!i&&this.audioCtx&&(this.audioCtx.close(),this.audioCtx=null,this.audioFilter=null,this.audioGain=null,this.audioBuffer=null)}setShowSwitch(i){if(this.showSwitch=i,this.hapticLabel){let t=this.hapticLabel.querySelector("input");this.hapticLabel.style.display=i?"":"none",t&&(t.style.display=i?"":"none")}}stopPattern(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.patternResolve?.(),this.patternResolve=null}runPattern(i,t,e){return new Promise(n=>{this.patternResolve=n;let o=[],s=0;for(let l of i){let p=Math.max(0,Math.min(1,l.intensity??t)),d=l.delay??0;d>0&&(s+=d,o.push({end:s,isOn:!1,intensity:0})),s+=l.duration,o.push({end:s,isOn:!0,intensity:p})}let r=s,u=0,c=-1,m=l=>{u===0&&(u=l);let p=l-u;if(p>=r){this.rafId=null,this.patternResolve=null,n();return}let d=o[0];for(let f of o)if(p<f.end){d=f;break}if(d.isOn){let f=L+(1-d.intensity)*O;c===-1?(c=l,e||(this.hapticLabel?.click(),this.debug&&this.audioCtx&&this.playClick(d.intensity),e=!0)):l-c>=f&&(this.hapticLabel?.click(),this.debug&&this.audioCtx&&this.playClick(d.intensity),c=l)}this.rafId=requestAnimationFrame(m)};this.rafId=requestAnimationFrame(m)})}playClick(i){if(!this.audioCtx||!this.audioFilter||!this.audioGain||!this.audioBuffer)return;let t=this.audioBuffer.getChannelData(0);for(let s=0;s<t.length;s++)t[s]=(Math.random()*2-1)*Math.exp(-s/25);this.audioGain.gain.value=.5*i;let e=2e3+i*2e3,n=1+(Math.random()-.5)*.3;this.audioFilter.frequency.value=e*n;let o=this.audioCtx.createBufferSource();o.buffer=this.audioBuffer,o.connect(this.audioFilter),o.onended=()=>o.disconnect(),o.start()}async ensureAudio(){if(!this.audioCtx&&typeof AudioContext<"u"){this.audioCtx=new AudioContext,this.audioFilter=this.audioCtx.createBiquadFilter(),this.audioFilter.type="bandpass",this.audioFilter.frequency.value=4e3,this.audioFilter.Q.value=8,this.audioGain=this.audioCtx.createGain(),this.audioFilter.connect(this.audioGain),this.audioGain.connect(this.audioCtx.destination);let i=.004;this.audioBuffer=this.audioCtx.createBuffer(1,this.audioCtx.sampleRate*i,this.audioCtx.sampleRate);let t=this.audioBuffer.getChannelData(0);for(let e=0;e<t.length;e++)t[e]=(Math.random()*2-1)*Math.exp(-e/25)}this.audioCtx?.state==="suspended"&&await this.audioCtx.resume()}ensureDOM(){if(this.domInitialized||typeof document>"u")return;let i=`web-haptics-${this.instanceId}`,t=document.createElement("label");t.setAttribute("for",i),t.textContent="Haptic feedback",t.style.position="fixed",t.style.bottom="10px",t.style.left="10px",t.style.padding="5px 10px",t.style.backgroundColor="rgba(0, 0, 0, 0.7)",t.style.color="white",t.style.fontFamily="sans-serif",t.style.fontSize="14px",t.style.borderRadius="4px",t.style.zIndex="9999",t.style.userSelect="none",this.hapticLabel=t;let e=document.createElement("input");e.type="checkbox",e.setAttribute("switch",""),e.id=i,e.style.all="initial",e.style.appearance="auto",this.showSwitch||(t.style.display="none",e.style.display="none"),t.appendChild(e),document.body.appendChild(t),this.domInitialized=!0}};function x(a){let i=new h(a),t=(r,u)=>i.trigger(r,u),e=()=>i.cancel(),n=()=>i.destroy(),o=r=>i.setDebug(r),s=h.isSupported;return{trigger:t,cancel:e,destroy:n,setDebug:o,isSupported:s}}0&&(module.exports={createWebHaptics});
|
package/dist/svelte/index.mjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
var m={success:{pattern:[{duration:
|
|
2
|
-
//# sourceMappingURL=index.mjs.map
|
|
1
|
+
var m={success:{pattern:[{duration:30,intensity:.5},{delay:60,duration:40,intensity:1}]},warning:{pattern:[{duration:40,intensity:.8},{delay:100,duration:40,intensity:.6}]},error:{pattern:[{duration:40,intensity:.9},{delay:40,duration:40,intensity:.9},{delay:40,duration:40,intensity:.9}]},light:{pattern:[{duration:15,intensity:.4}]},medium:{pattern:[{duration:25,intensity:.7}]},heavy:{pattern:[{duration:35,intensity:1}]},soft:{pattern:[{duration:40,intensity:.5}]},rigid:{pattern:[{duration:10,intensity:1}]},selection:{pattern:[{duration:8,intensity:.3}]},nudge:{pattern:[{duration:80,intensity:.8},{delay:80,duration:50,intensity:.3}]},buzz:{pattern:[{duration:1e3,intensity:1}]}};var v=16,x=184,g=1e3,b=20;function C(o){if(typeof o=="number")return{vibrations:[{duration:o}]};if(typeof o=="string"){let i=m[o];return i?{vibrations:i.pattern.map(t=>({...t}))}:(console.warn(`[web-haptics] Unknown preset: "${o}"`),null)}if(Array.isArray(o)){if(o.length===0)return{vibrations:[]};if(typeof o[0]=="number"){let i=o,t=[];for(let e=0;e<i.length;e+=2){let n=e>0?i[e-1]:0;t.push({...n>0&&{delay:n},duration:i[e]})}return{vibrations:t}}return{vibrations:o.map(i=>({...i}))}}return{vibrations:o.pattern.map(i=>({...i}))}}function w(o,i){if(i>=1)return[o];if(i<=0)return[];let t=Math.max(1,Math.round(b*i)),e=b-t,n=[],s=o;for(;s>=b;)n.push(t),n.push(e),s-=b;if(s>0){let a=Math.max(1,Math.round(s*i));n.push(a);let r=s-a;r>0&&n.push(r)}return n}function I(o,i){let t=[];for(let e=0;e<o.length;e++){let n=o[e],s=Math.max(0,Math.min(1,n.intensity??i)),a=n.delay??0;a>0&&(t.length>0&&t.length%2===0?t[t.length-1]+=a:(t.length===0&&t.push(0),t.push(a)));let r=w(n.duration,s);if(r.length===0){t.length>0&&t.length%2===0?t[t.length-1]+=n.duration:n.duration>0&&(t.push(0),t.push(n.duration));continue}for(let u of r)t.push(u)}return t}var M=0,h=class o{hapticLabel=null;domInitialized=!1;instanceId;debug;showSwitch;rafId=null;patternResolve=null;audioCtx=null;audioFilter=null;audioGain=null;audioBuffer=null;constructor(i){this.instanceId=++M,this.debug=i?.debug??!1,this.showSwitch=i?.showSwitch??!1}static isSupported=typeof navigator<"u"&&typeof navigator.vibrate=="function";async trigger(i=[{duration:25,intensity:.7}],t){let e=C(i);if(!e)return;let{vibrations:n}=e;if(n.length===0)return;let s=Math.max(0,Math.min(1,t?.intensity??.5));for(let a of n)if(a.duration>g&&(a.duration=g),!Number.isFinite(a.duration)||a.duration<0||a.delay!==void 0&&(!Number.isFinite(a.delay)||a.delay<0)){console.warn("[web-haptics] Invalid vibration values. Durations and delays must be finite non-negative numbers.");return}if(o.isSupported&&navigator.vibrate(I(n,s)),!o.isSupported||this.debug){if(this.ensureDOM(),!this.hapticLabel)return;this.debug&&await this.ensureAudio(),this.stopPattern();let r=(n[0]?.delay??0)===0;if(r&&(this.hapticLabel.click(),this.debug&&this.audioCtx)){let u=Math.max(0,Math.min(1,n[0].intensity??s));this.playClick(u)}await this.runPattern(n,s,r)}}cancel(){this.stopPattern(),o.isSupported&&navigator.vibrate(0)}destroy(){this.stopPattern(),this.hapticLabel&&(this.hapticLabel.remove(),this.hapticLabel=null,this.domInitialized=!1),this.audioCtx&&(this.audioCtx.close(),this.audioCtx=null,this.audioFilter=null,this.audioGain=null,this.audioBuffer=null)}setDebug(i){this.debug=i,!i&&this.audioCtx&&(this.audioCtx.close(),this.audioCtx=null,this.audioFilter=null,this.audioGain=null,this.audioBuffer=null)}setShowSwitch(i){if(this.showSwitch=i,this.hapticLabel){let t=this.hapticLabel.querySelector("input");this.hapticLabel.style.display=i?"":"none",t&&(t.style.display=i?"":"none")}}stopPattern(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.patternResolve?.(),this.patternResolve=null}runPattern(i,t,e){return new Promise(n=>{this.patternResolve=n;let s=[],a=0;for(let l of i){let p=Math.max(0,Math.min(1,l.intensity??t)),d=l.delay??0;d>0&&(a+=d,s.push({end:a,isOn:!1,intensity:0})),a+=l.duration,s.push({end:a,isOn:!0,intensity:p})}let r=a,u=0,c=-1,y=l=>{u===0&&(u=l);let p=l-u;if(p>=r){this.rafId=null,this.patternResolve=null,n();return}let d=s[0];for(let f of s)if(p<f.end){d=f;break}if(d.isOn){let f=v+(1-d.intensity)*x;c===-1?(c=l,e||(this.hapticLabel?.click(),this.debug&&this.audioCtx&&this.playClick(d.intensity),e=!0)):l-c>=f&&(this.hapticLabel?.click(),this.debug&&this.audioCtx&&this.playClick(d.intensity),c=l)}this.rafId=requestAnimationFrame(y)};this.rafId=requestAnimationFrame(y)})}playClick(i){if(!this.audioCtx||!this.audioFilter||!this.audioGain||!this.audioBuffer)return;let t=this.audioBuffer.getChannelData(0);for(let a=0;a<t.length;a++)t[a]=(Math.random()*2-1)*Math.exp(-a/25);this.audioGain.gain.value=.5*i;let e=2e3+i*2e3,n=1+(Math.random()-.5)*.3;this.audioFilter.frequency.value=e*n;let s=this.audioCtx.createBufferSource();s.buffer=this.audioBuffer,s.connect(this.audioFilter),s.onended=()=>s.disconnect(),s.start()}async ensureAudio(){if(!this.audioCtx&&typeof AudioContext<"u"){this.audioCtx=new AudioContext,this.audioFilter=this.audioCtx.createBiquadFilter(),this.audioFilter.type="bandpass",this.audioFilter.frequency.value=4e3,this.audioFilter.Q.value=8,this.audioGain=this.audioCtx.createGain(),this.audioFilter.connect(this.audioGain),this.audioGain.connect(this.audioCtx.destination);let i=.004;this.audioBuffer=this.audioCtx.createBuffer(1,this.audioCtx.sampleRate*i,this.audioCtx.sampleRate);let t=this.audioBuffer.getChannelData(0);for(let e=0;e<t.length;e++)t[e]=(Math.random()*2-1)*Math.exp(-e/25)}this.audioCtx?.state==="suspended"&&await this.audioCtx.resume()}ensureDOM(){if(this.domInitialized||typeof document>"u")return;let i=`web-haptics-${this.instanceId}`,t=document.createElement("label");t.setAttribute("for",i),t.textContent="Haptic feedback",t.style.position="fixed",t.style.bottom="10px",t.style.left="10px",t.style.padding="5px 10px",t.style.backgroundColor="rgba(0, 0, 0, 0.7)",t.style.color="white",t.style.fontFamily="sans-serif",t.style.fontSize="14px",t.style.borderRadius="4px",t.style.zIndex="9999",t.style.userSelect="none",this.hapticLabel=t;let e=document.createElement("input");e.type="checkbox",e.setAttribute("switch",""),e.id=i,e.style.all="initial",e.style.appearance="auto",this.showSwitch||(t.style.display="none",e.style.display="none"),t.appendChild(e),document.body.appendChild(t),this.domInitialized=!0}};function F(o){let i=new h(o),t=(r,u)=>i.trigger(r,u),e=()=>i.cancel(),n=()=>i.destroy(),s=r=>i.setDebug(r),a=h.isSupported;return{trigger:t,cancel:e,destroy:n,setDebug:s,isSupported:a}}export{F as createWebHaptics};
|
package/dist/vue/index.d.mts
CHANGED
package/dist/vue/index.d.ts
CHANGED
package/dist/vue/index.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
"use strict";var m=Object.defineProperty;var w=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var M=Object.prototype.hasOwnProperty;var F=(a,i)=>{for(var t in i)m(a,t,{get:i[t],enumerable:!0})},S=(a,i,t,e)=>{if(i&&typeof i=="object"||typeof i=="function")for(let n of I(i))!M.call(a,n)&&n!==t&&m(a,n,{get:()=>i[n],enumerable:!(e=w(i,n))||e.enumerable});return a};var L=a=>S(m({},"__esModule",{value:!0}),a);var B={};F(B,{useWebHaptics:()=>C});module.exports=L(B);var h=require("vue");var v={success:{pattern:[{duration:30,intensity:.5},{delay:60,duration:40,intensity:1}]},warning:{pattern:[{duration:40,intensity:.8},{delay:100,duration:40,intensity:.6}]},error:{pattern:[{duration:40,intensity:.9},{delay:40,duration:40,intensity:.9},{delay:40,duration:40,intensity:.9}]},light:{pattern:[{duration:15,intensity:.4}]},medium:{pattern:[{duration:25,intensity:.7}]},heavy:{pattern:[{duration:35,intensity:1}]},soft:{pattern:[{duration:40,intensity:.5}]},rigid:{pattern:[{duration:10,intensity:1}]},selection:{pattern:[{duration:8,intensity:.3}]},nudge:{pattern:[{duration:80,intensity:.8},{delay:80,duration:50,intensity:.3}]},buzz:{pattern:[{duration:1e3,intensity:1}]}};var O=16,P=184,x=1e3,y=20;function H(a){if(typeof a=="number")return{vibrations:[{duration:a}]};if(typeof a=="string"){let i=v[a];return i?{vibrations:i.pattern.map(t=>({...t}))}:(console.warn(`[web-haptics] Unknown preset: "${a}"`),null)}if(Array.isArray(a)){if(a.length===0)return{vibrations:[]};if(typeof a[0]=="number"){let i=a,t=[];for(let e=0;e<i.length;e+=2){let n=e>0?i[e-1]:0;t.push({...n>0&&{delay:n},duration:i[e]})}return{vibrations:t}}return{vibrations:a.map(i=>({...i}))}}return{vibrations:a.pattern.map(i=>({...i}))}}function A(a,i){if(i>=1)return[a];if(i<=0)return[];let t=Math.max(1,Math.round(y*i)),e=y-t,n=[],o=a;for(;o>=y;)n.push(t),n.push(e),o-=y;if(o>0){let s=Math.max(1,Math.round(o*i));n.push(s);let r=o-s;r>0&&n.push(r)}return n}function k(a,i){let t=[];for(let e=0;e<a.length;e++){let n=a[e],o=Math.max(0,Math.min(1,n.intensity??i)),s=n.delay??0;s>0&&(t.length>0&&t.length%2===0?t[t.length-1]+=s:(t.length===0&&t.push(0),t.push(s)));let r=A(n.duration,o);if(r.length===0){t.length>0&&t.length%2===0?t[t.length-1]+=n.duration:n.duration>0&&(t.push(0),t.push(n.duration));continue}for(let d of r)t.push(d)}return t}var G=0,c=class a{hapticLabel=null;domInitialized=!1;instanceId;debug;showSwitch;rafId=null;patternResolve=null;audioCtx=null;audioFilter=null;audioGain=null;audioBuffer=null;constructor(i){this.instanceId=++G,this.debug=i?.debug??!1,this.showSwitch=i?.showSwitch??!1}static isSupported=typeof navigator<"u"&&typeof navigator.vibrate=="function";async trigger(i=[{duration:25,intensity:.7}],t){let e=H(i);if(!e)return;let{vibrations:n}=e;if(n.length===0)return;let o=Math.max(0,Math.min(1,t?.intensity??.5));for(let s of n)if(s.duration>x&&(s.duration=x),!Number.isFinite(s.duration)||s.duration<0||s.delay!==void 0&&(!Number.isFinite(s.delay)||s.delay<0)){console.warn("[web-haptics] Invalid vibration values. Durations and delays must be finite non-negative numbers.");return}if(a.isSupported&&navigator.vibrate(k(n,o)),!a.isSupported||this.debug){if(this.ensureDOM(),!this.hapticLabel)return;this.debug&&await this.ensureAudio(),this.stopPattern();let r=(n[0]?.delay??0)===0;if(r&&(this.hapticLabel.click(),this.debug&&this.audioCtx)){let d=Math.max(0,Math.min(1,n[0].intensity??o));this.playClick(d)}await this.runPattern(n,o,r)}}cancel(){this.stopPattern(),a.isSupported&&navigator.vibrate(0)}destroy(){this.stopPattern(),this.hapticLabel&&(this.hapticLabel.remove(),this.hapticLabel=null,this.domInitialized=!1),this.audioCtx&&(this.audioCtx.close(),this.audioCtx=null,this.audioFilter=null,this.audioGain=null,this.audioBuffer=null)}setDebug(i){this.debug=i,!i&&this.audioCtx&&(this.audioCtx.close(),this.audioCtx=null,this.audioFilter=null,this.audioGain=null,this.audioBuffer=null)}setShowSwitch(i){if(this.showSwitch=i,this.hapticLabel){let t=this.hapticLabel.querySelector("input");this.hapticLabel.style.display=i?"":"none",t&&(t.style.display=i?"":"none")}}stopPattern(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.patternResolve?.(),this.patternResolve=null}runPattern(i,t,e){return new Promise(n=>{this.patternResolve=n;let o=[],s=0;for(let u of i){let f=Math.max(0,Math.min(1,u.intensity??t)),l=u.delay??0;l>0&&(s+=l,o.push({end:s,isOn:!1,intensity:0})),s+=u.duration,o.push({end:s,isOn:!0,intensity:f})}let r=s,d=0,p=-1,g=u=>{d===0&&(d=u);let f=u-d;if(f>=r){this.rafId=null,this.patternResolve=null,n();return}let l=o[0];for(let b of o)if(f<b.end){l=b;break}if(l.isOn){let b=O+(1-l.intensity)*P;p===-1?(p=u,e||(this.hapticLabel?.click(),this.debug&&this.audioCtx&&this.playClick(l.intensity),e=!0)):u-p>=b&&(this.hapticLabel?.click(),this.debug&&this.audioCtx&&this.playClick(l.intensity),p=u)}this.rafId=requestAnimationFrame(g)};this.rafId=requestAnimationFrame(g)})}playClick(i){if(!this.audioCtx||!this.audioFilter||!this.audioGain||!this.audioBuffer)return;let t=this.audioBuffer.getChannelData(0);for(let s=0;s<t.length;s++)t[s]=(Math.random()*2-1)*Math.exp(-s/25);this.audioGain.gain.value=.5*i;let e=2e3+i*2e3,n=1+(Math.random()-.5)*.3;this.audioFilter.frequency.value=e*n;let o=this.audioCtx.createBufferSource();o.buffer=this.audioBuffer,o.connect(this.audioFilter),o.onended=()=>o.disconnect(),o.start()}async ensureAudio(){if(!this.audioCtx&&typeof AudioContext<"u"){this.audioCtx=new AudioContext,this.audioFilter=this.audioCtx.createBiquadFilter(),this.audioFilter.type="bandpass",this.audioFilter.frequency.value=4e3,this.audioFilter.Q.value=8,this.audioGain=this.audioCtx.createGain(),this.audioFilter.connect(this.audioGain),this.audioGain.connect(this.audioCtx.destination);let i=.004;this.audioBuffer=this.audioCtx.createBuffer(1,this.audioCtx.sampleRate*i,this.audioCtx.sampleRate);let t=this.audioBuffer.getChannelData(0);for(let e=0;e<t.length;e++)t[e]=(Math.random()*2-1)*Math.exp(-e/25)}this.audioCtx?.state==="suspended"&&await this.audioCtx.resume()}ensureDOM(){if(this.domInitialized||typeof document>"u")return;let i=`web-haptics-${this.instanceId}`,t=document.createElement("label");t.setAttribute("for",i),t.textContent="Haptic feedback",t.style.position="fixed",t.style.bottom="10px",t.style.left="10px",t.style.padding="5px 10px",t.style.backgroundColor="rgba(0, 0, 0, 0.7)",t.style.color="white",t.style.fontFamily="sans-serif",t.style.fontSize="14px",t.style.borderRadius="4px",t.style.zIndex="9999",t.style.userSelect="none",this.hapticLabel=t;let e=document.createElement("input");e.type="checkbox",e.setAttribute("switch",""),e.id=i,e.style.all="initial",e.style.appearance="auto",this.showSwitch||(t.style.display="none",e.style.display="none"),t.appendChild(e),document.body.appendChild(t),this.domInitialized=!0}};function C(a){let i=null;(0,h.onMounted)(()=>{i=new c(a)}),(0,h.onUnmounted)(()=>{i?.destroy(),i=null}),(0,h.watch)(()=>a?.debug,o=>{i?.setDebug(o??!1)});let t=(o,s)=>i?.trigger(o,s),e=()=>i?.cancel(),n=c.isSupported;return{trigger:t,cancel:e,isSupported:n}}0&&(module.exports={useWebHaptics});
|