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 CHANGED
@@ -59,34 +59,57 @@ haptics.trigger("success");
59
59
 
60
60
  ## Built-in Presets
61
61
 
62
- | Name | Pattern | Intensity |
63
- | --------- | ---------------------- | --------- |
64
- | `success` | `[50, 50, 50]` | 0.5 |
65
- | `nudge` | `[80, 100, 200]` | 0.5 |
66
- | `error` | `[50, 50, 50, 50, 50]` | 0.75 |
67
- | `buzz` | `[1000]` | 1.0 |
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]); // custom pattern
73
- trigger(200); // single vibration
74
- trigger({ pattern: [50, 50, 50], description: "custom", intensity: 0.8 }); // full preset
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
- ### `trigger(input?, options?)`
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"`), number, `number[]`, or `HapticPreset`
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-DkzcH7AA.mjs';
2
- export { a as HapticPattern, b as HapticPreset, V as Vibration } from './types-DkzcH7AA.mjs';
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";
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: 50;
35
+ readonly duration: 30;
36
+ readonly intensity: 0.5;
36
37
  }, {
37
- readonly delay: 50;
38
- readonly duration: 50;
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 nudge: {
43
+ readonly warning: {
43
44
  readonly pattern: [{
44
- readonly duration: 80;
45
+ readonly duration: 40;
45
46
  readonly intensity: 0.8;
46
47
  }, {
47
- readonly delay: 80;
48
- readonly duration: 50;
49
- readonly intensity: 0.3;
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: 50;
56
- readonly intensity: 0.75;
55
+ readonly duration: 40;
56
+ readonly intensity: 0.9;
57
57
  }, {
58
- readonly delay: 50;
59
- readonly duration: 50;
60
- readonly intensity: 0.75;
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: 50;
108
+ readonly delay: 80;
63
109
  readonly duration: 50;
64
- readonly intensity: 0.75;
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-DkzcH7AA.js';
2
- export { a as HapticPattern, b as HapticPreset, V as Vibration } from './types-DkzcH7AA.js';
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";
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: 50;
35
+ readonly duration: 30;
36
+ readonly intensity: 0.5;
36
37
  }, {
37
- readonly delay: 50;
38
- readonly duration: 50;
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 nudge: {
43
+ readonly warning: {
43
44
  readonly pattern: [{
44
- readonly duration: 80;
45
+ readonly duration: 40;
45
46
  readonly intensity: 0.8;
46
47
  }, {
47
- readonly delay: 80;
48
- readonly duration: 50;
49
- readonly intensity: 0.3;
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: 50;
56
- readonly intensity: 0.75;
55
+ readonly duration: 40;
56
+ readonly intensity: 0.9;
57
57
  }, {
58
- readonly delay: 50;
59
- readonly duration: 50;
60
- readonly intensity: 0.75;
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: 50;
108
+ readonly delay: 80;
63
109
  readonly duration: 50;
64
- readonly intensity: 0.75;
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 y=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var M=(s,i)=>{for(var t in i)y(s,t,{get:i[t],enumerable:!0})},A=(s,i,t,e)=>{if(i&&typeof i=="object"||typeof i=="function")for(let n of w(i))!I.call(s,n)&&n!==t&&y(s,n,{get:()=>i[n],enumerable:!(e=C(i,n))||e.enumerable});return s};var P=s=>A(y({},"__esModule",{value:!0}),s);var B={};M(B,{WebHaptics:()=>m,defaultPatterns:()=>f,version:()=>g});module.exports=P(B);var g="0.0.4";var f={success:{pattern:[{duration:50},{delay:50,duration:50}],description:"A series of taps indicating success."},nudge:{pattern:[{duration:80,intensity:.8},{delay:80,duration:50,intensity:.3}],description:"A series of taps indicating a nudge."},error:{pattern:[{duration:50,intensity:.75},{delay:50,duration:50,intensity:.75},{delay:50,duration:50,intensity:.75}],description:"A series of taps indicating a warning or error."},buzz:{pattern:[{duration:1e3,intensity:1}],description:"A long vibration."}};var L=16,F=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 G=0,m=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=++G,this.debug=i?.debug??!1,this.showSwitch=i?.showSwitch??!1}static isSupported=typeof navigator<"u"&&typeof navigator.vibrate=="function";async trigger(i=[{duration:10}],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)*F;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 n=0;n<t.length;n++)t[n]=(Math.random()*2-1)*Math.exp(-n/25);this.audioGain.gain.value=.5*i;let e=this.audioCtx.createBufferSource();e.buffer=this.audioBuffer,e.connect(this.audioFilter),e.onended=()=>e.disconnect(),e.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});
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-4RZ72ZZU.mjs";var s="0.0.4";export{t as WebHaptics,e as defaultPatterns,s as version};
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};
@@ -1,4 +1,4 @@
1
- import { W as WebHapticsOptions, H as HapticInput, T as TriggerOptions } from '../types-DkzcH7AA.mjs';
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;
@@ -1,4 +1,4 @@
1
- import { W as WebHapticsOptions, H as HapticInput, T as TriggerOptions } from '../types-DkzcH7AA.js';
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;
@@ -1,3 +1,2 @@
1
1
  "use client";
2
- "use strict";var y=Object.defineProperty;var w=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var M=Object.prototype.hasOwnProperty;var S=(a,i)=>{for(var t in i)y(a,t,{get:i[t],enumerable:!0})},A=(a,i,t,e)=>{if(i&&typeof i=="object"||typeof i=="function")for(let n of I(i))!M.call(a,n)&&n!==t&&y(a,n,{get:()=>i[n],enumerable:!(e=w(i,n))||e.enumerable});return a};var F=a=>A(y({},"__esModule",{value:!0}),a);var B={};S(B,{useWebHaptics:()=>C});module.exports=F(B);var l=require("react");var v={success:{pattern:[{duration:50},{delay:50,duration:50}],description:"A series of taps indicating success."},nudge:{pattern:[{duration:80,intensity:.8},{delay:80,duration:50,intensity:.3}],description:"A series of taps indicating a nudge."},error:{pattern:[{duration:50,intensity:.75},{delay:50,duration:50,intensity:.75},{delay:50,duration:50,intensity:.75}],description:"A series of taps indicating a warning or error."},buzz:{pattern:[{duration:1e3,intensity:1}],description:"A long vibration."}};var L=16,O=184,x=1e3,m=20;function P(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 H(a,i){if(i>=1)return[a];if(i<=0)return[];let t=Math.max(1,Math.round(m*i)),e=m-t,n=[],r=a;for(;r>=m;)n.push(t),n.push(e),r-=m;if(r>0){let s=Math.max(1,Math.round(r*i));n.push(s);let o=r-s;o>0&&n.push(o)}return n}function k(a,i){let t=[];for(let e=0;e<a.length;e++){let n=a[e],r=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 o=H(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 c of o)t.push(c)}return t}var G=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=++G,this.debug=i?.debug??!1,this.showSwitch=i?.showSwitch??!1}static isSupported=typeof navigator<"u"&&typeof navigator.vibrate=="function";async trigger(i=[{duration:10}],t){let e=P(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 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,r)),!a.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 c=Math.max(0,Math.min(1,n[0].intensity??r));this.playClick(c)}await this.runPattern(n,r,o)}}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 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,n();return}let d=r[0];for(let b of r)if(f<b.end){d=b;break}if(d.isOn){let b=L+(1-d.intensity)*O;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 n=0;n<t.length;n++)t[n]=(Math.random()*2-1)*Math.exp(-n/25);this.audioGain.gain.value=.5*i;let e=this.audioCtx.createBufferSource();e.buffer=this.audioBuffer,e.connect(this.audioFilter),e.onended=()=>e.disconnect(),e.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=(0,l.useRef)(null);(0,l.useEffect)(()=>(i.current=new h(a),()=>{i.current?.destroy(),i.current=null}),[]),(0,l.useEffect)(()=>{i.current?.setDebug(a?.debug??!1)},[a?.debug]),(0,l.useEffect)(()=>{i.current?.setShowSwitch(a?.showSwitch??!1)},[a?.showSwitch]);let t=(0,l.useCallback)((r,s)=>i.current?.trigger(r,s),[]),e=(0,l.useCallback)(()=>i.current?.cancel(),[]),n=h.isSupported;return{trigger:t,cancel:e,isSupported:n}}0&&(module.exports={useWebHaptics});
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});
@@ -1,3 +1,2 @@
1
1
  "use client";
2
- import{b as r}from"../chunk-4RZ72ZZU.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};
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};
@@ -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})},A=(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=>A(y({},"__esModule",{value:!0}),a);var G={};M(G,{createWebHaptics:()=>x});module.exports=S(G);var g={success:{pattern:[{duration:50},{delay:50,duration:50}],description:"A series of taps indicating success."},nudge:{pattern:[{duration:80,intensity:.8},{delay:80,duration:50,intensity:.3}],description:"A series of taps indicating a nudge."},error:{pattern:[{duration:50,intensity:.75},{delay:50,duration:50,intensity:.75},{delay:50,duration:50,intensity:.75}],description:"A series of taps indicating a warning or error."},buzz:{pattern:[{duration:1e3,intensity:1}],description:"A long vibration."}};var F=16,L=184,v=1e3,b=20;function O(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 P(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=P(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,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=++k,this.debug=i?.debug??!1,this.showSwitch=i?.showSwitch??!1}static isSupported=typeof navigator<"u"&&typeof navigator.vibrate=="function";async trigger(i=[{duration:10}],t){let e=O(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,h=-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=F+(1-d.intensity)*L;h===-1?(h=l,e||(this.hapticLabel?.click(),this.debug&&this.audioCtx&&this.playClick(d.intensity),e=!0)):l-h>=f&&(this.hapticLabel?.click(),this.debug&&this.audioCtx&&this.playClick(d.intensity),h=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 n=0;n<t.length;n++)t[n]=(Math.random()*2-1)*Math.exp(-n/25);this.audioGain.gain.value=.5*i;let e=this.audioCtx.createBufferSource();e.buffer=this.audioBuffer,e.connect(this.audioFilter),e.onended=()=>e.disconnect(),e.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 c(a),t=(r,u)=>i.trigger(r,u),e=()=>i.cancel(),n=()=>i.destroy(),o=r=>i.setDebug(r),s=c.isSupported;return{trigger:t,cancel:e,destroy:n,setDebug:o,isSupported:s}}0&&(module.exports={createWebHaptics});
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});
@@ -1,2 +1 @@
1
- var m={success:{pattern:[{duration:50},{delay:50,duration:50}],description:"A series of taps indicating success."},nudge:{pattern:[{duration:80,intensity:.8},{delay:80,duration:50,intensity:.3}],description:"A series of taps indicating a nudge."},error:{pattern:[{duration:50,intensity:.75},{delay:50,duration:50,intensity:.75},{delay:50,duration:50,intensity:.75}],description:"A series of taps indicating a warning or error."},buzz:{pattern:[{duration:1e3,intensity:1}],description:"A long vibration."}};var v=16,x=184,g=1e3,b=20;function C(a){if(typeof a=="number")return{vibrations:[{duration:a}]};if(typeof a=="string"){let i=m[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 w(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 I(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=w(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 M=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=++M,this.debug=i?.debug??!1,this.showSwitch=i?.showSwitch??!1}static isSupported=typeof navigator<"u"&&typeof navigator.vibrate=="function";async trigger(i=[{duration:10}],t){let e=C(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>g&&(s.duration=g),!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(I(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,h=-1,y=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=v+(1-d.intensity)*x;h===-1?(h=l,e||(this.hapticLabel?.click(),this.debug&&this.audioCtx&&this.playClick(d.intensity),e=!0)):l-h>=f&&(this.hapticLabel?.click(),this.debug&&this.audioCtx&&this.playClick(d.intensity),h=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 n=0;n<t.length;n++)t[n]=(Math.random()*2-1)*Math.exp(-n/25);this.audioGain.gain.value=.5*i;let e=this.audioCtx.createBufferSource();e.buffer=this.audioBuffer,e.connect(this.audioFilter),e.onended=()=>e.disconnect(),e.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 A(a){let i=new c(a),t=(r,u)=>i.trigger(r,u),e=()=>i.cancel(),n=()=>i.destroy(),o=r=>i.setDebug(r),s=c.isSupported;return{trigger:t,cancel:e,destroy:n,setDebug:o,isSupported:s}}export{A as createWebHaptics};
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};
@@ -6,7 +6,6 @@ interface Vibration {
6
6
  type HapticPattern = number[] | Vibration[];
7
7
  interface HapticPreset {
8
8
  pattern: Vibration[];
9
- description: string;
10
9
  }
11
10
  type HapticInput = number | string | HapticPattern | HapticPreset;
12
11
  interface TriggerOptions {
@@ -6,7 +6,6 @@ interface Vibration {
6
6
  type HapticPattern = number[] | Vibration[];
7
7
  interface HapticPreset {
8
8
  pattern: Vibration[];
9
- description: string;
10
9
  }
11
10
  type HapticInput = number | string | HapticPattern | HapticPreset;
12
11
  interface TriggerOptions {
@@ -6,7 +6,6 @@ interface Vibration {
6
6
  type HapticPattern = number[] | Vibration[];
7
7
  interface HapticPreset {
8
8
  pattern: Vibration[];
9
- description: string;
10
9
  }
11
10
  type HapticInput = number | string | HapticPattern | HapticPreset;
12
11
  interface TriggerOptions {
@@ -6,7 +6,6 @@ interface Vibration {
6
6
  type HapticPattern = number[] | Vibration[];
7
7
  interface HapticPreset {
8
8
  pattern: Vibration[];
9
- description: string;
10
9
  }
11
10
  type HapticInput = number | string | HapticPattern | HapticPreset;
12
11
  interface TriggerOptions {
package/dist/vue/index.js CHANGED
@@ -1,2 +1 @@
1
- "use strict";var y=Object.defineProperty;var w=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var M=Object.prototype.hasOwnProperty;var A=(a,i)=>{for(var t in i)y(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&&y(a,n,{get:()=>i[n],enumerable:!(e=w(i,n))||e.enumerable});return a};var F=a=>S(y({},"__esModule",{value:!0}),a);var B={};A(B,{useWebHaptics:()=>C});module.exports=F(B);var c=require("vue");var v={success:{pattern:[{duration:50},{delay:50,duration:50}],description:"A series of taps indicating success."},nudge:{pattern:[{duration:80,intensity:.8},{delay:80,duration:50,intensity:.3}],description:"A series of taps indicating a nudge."},error:{pattern:[{duration:50,intensity:.75},{delay:50,duration:50,intensity:.75},{delay:50,duration:50,intensity:.75}],description:"A series of taps indicating a warning or error."},buzz:{pattern:[{duration:1e3,intensity:1}],description:"A long vibration."}};var L=16,O=184,x=1e3,m=20;function P(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 H(a,i){if(i>=1)return[a];if(i<=0)return[];let t=Math.max(1,Math.round(m*i)),e=m-t,n=[],o=a;for(;o>=m;)n.push(t),n.push(e),o-=m;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=H(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,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=++G,this.debug=i?.debug??!1,this.showSwitch=i?.showSwitch??!1}static isSupported=typeof navigator<"u"&&typeof navigator.vibrate=="function";async trigger(i=[{duration:10}],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>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=L+(1-l.intensity)*O;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 n=0;n<t.length;n++)t[n]=(Math.random()*2-1)*Math.exp(-n/25);this.audioGain.gain.value=.5*i;let e=this.audioCtx.createBufferSource();e.buffer=this.audioBuffer,e.connect(this.audioFilter),e.onended=()=>e.disconnect(),e.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,c.onMounted)(()=>{i=new h(a)}),(0,c.onUnmounted)(()=>{i?.destroy(),i=null}),(0,c.watch)(()=>a?.debug,o=>{i?.setDebug(o??!1)});let t=(o,s)=>i?.trigger(o,s),e=()=>i?.cancel(),n=h.isSupported;return{trigger:t,cancel:e,isSupported:n}}0&&(module.exports={useWebHaptics});
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});