@hapticjs/core 0.1.0 → 0.2.1

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/dist/index.js CHANGED
@@ -32,6 +32,66 @@ var NoopAdapter = class {
32
32
  }
33
33
  };
34
34
 
35
+ // src/adapters/web-vibration.adapter.ts
36
+ var WebVibrationAdapter = class {
37
+ constructor() {
38
+ this.name = "web-vibration";
39
+ this.supported = typeof navigator !== "undefined" && "vibrate" in navigator;
40
+ }
41
+ capabilities() {
42
+ return {
43
+ maxIntensityLevels: 1,
44
+ minDuration: 20,
45
+ maxDuration: 1e4,
46
+ supportsPattern: true,
47
+ supportsIntensity: false,
48
+ dualMotor: false
49
+ };
50
+ }
51
+ async pulse(_intensity, duration) {
52
+ if (!this.supported) return;
53
+ navigator.vibrate(Math.max(duration, 20));
54
+ }
55
+ async playSequence(steps) {
56
+ if (!this.supported || steps.length === 0) return;
57
+ const pattern = [];
58
+ let lastType = null;
59
+ for (const step of steps) {
60
+ if (step.type === "vibrate" && step.intensity > 0.05) {
61
+ const dur = Math.max(step.duration, 20);
62
+ if (lastType === "vibrate") {
63
+ pattern[pattern.length - 1] += dur;
64
+ } else {
65
+ pattern.push(dur);
66
+ }
67
+ lastType = "vibrate";
68
+ } else {
69
+ const dur = Math.max(step.duration, 10);
70
+ if (lastType === "pause") {
71
+ pattern[pattern.length - 1] += dur;
72
+ } else {
73
+ pattern.push(dur);
74
+ }
75
+ lastType = "pause";
76
+ }
77
+ }
78
+ if (pattern.length > 0) {
79
+ if (steps[0]?.type === "pause" || steps[0]?.type === "vibrate" && steps[0]?.intensity <= 0.05) {
80
+ pattern.unshift(0);
81
+ }
82
+ navigator.vibrate(pattern);
83
+ }
84
+ }
85
+ cancel() {
86
+ if (this.supported) {
87
+ navigator.vibrate(0);
88
+ }
89
+ }
90
+ dispose() {
91
+ this.cancel();
92
+ }
93
+ };
94
+
35
95
  // src/utils/scheduling.ts
36
96
  function delay(ms) {
37
97
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -43,49 +103,37 @@ function normalizeIntensity(intensity) {
43
103
  return clamp(intensity, 0, 1);
44
104
  }
45
105
 
46
- // src/adapters/web-vibration.adapter.ts
47
- var WebVibrationAdapter = class {
106
+ // src/adapters/ios-audio.adapter.ts
107
+ var IoSAudioAdapter = class {
48
108
  constructor() {
49
- this.name = "web-vibration";
109
+ this.name = "ios-audio";
110
+ this._audioCtx = null;
111
+ this._activeOscillator = null;
112
+ this._activeGain = null;
50
113
  this._cancelled = false;
51
- this.supported = typeof navigator !== "undefined" && "vibrate" in navigator;
114
+ this.supported = this._detectSupport();
52
115
  }
53
116
  capabilities() {
54
117
  return {
55
- maxIntensityLevels: 1,
56
- // on/off only
57
- minDuration: 10,
58
- maxDuration: 1e4,
118
+ maxIntensityLevels: 100,
119
+ minDuration: 5,
120
+ maxDuration: 5e3,
59
121
  supportsPattern: true,
60
- supportsIntensity: false,
122
+ supportsIntensity: true,
61
123
  dualMotor: false
62
124
  };
63
125
  }
64
- async pulse(_intensity, duration) {
126
+ async pulse(intensity, duration) {
65
127
  if (!this.supported) return;
66
- navigator.vibrate(duration);
128
+ await this._playTone(intensity, duration);
67
129
  }
68
130
  async playSequence(steps) {
69
131
  if (!this.supported || steps.length === 0) return;
70
132
  this._cancelled = false;
71
- const pattern = this._toVibrationPattern(steps);
72
- if (this._canUseNativePattern(steps)) {
73
- navigator.vibrate(pattern);
74
- return;
75
- }
76
133
  for (const step of steps) {
77
134
  if (this._cancelled) break;
78
- if (step.type === "vibrate") {
79
- if (step.intensity > 0.1) {
80
- if (step.intensity < 0.5) {
81
- await this._pwmVibrate(step.duration, step.intensity);
82
- } else {
83
- navigator.vibrate(step.duration);
84
- await delay(step.duration);
85
- }
86
- } else {
87
- await delay(step.duration);
88
- }
135
+ if (step.type === "vibrate" && step.intensity > 0) {
136
+ await this._playTone(step.intensity, step.duration);
89
137
  } else {
90
138
  await delay(step.duration);
91
139
  }
@@ -93,40 +141,82 @@ var WebVibrationAdapter = class {
93
141
  }
94
142
  cancel() {
95
143
  this._cancelled = true;
96
- if (this.supported) {
97
- navigator.vibrate(0);
98
- }
144
+ this._stopOscillator();
99
145
  }
100
146
  dispose() {
101
147
  this.cancel();
102
- }
103
- /** Convert steps to Vibration API pattern array */
104
- _toVibrationPattern(steps) {
105
- const pattern = [];
106
- for (const step of steps) {
107
- pattern.push(step.duration);
148
+ if (this._audioCtx) {
149
+ void this._audioCtx.close();
150
+ this._audioCtx = null;
108
151
  }
109
- return pattern;
110
152
  }
111
- /** Check if all steps can be played with native pattern (no intensity variation) */
112
- _canUseNativePattern(steps) {
113
- return steps.every(
114
- (s) => s.type === "pause" || s.type === "vibrate" && s.intensity >= 0.5
153
+ // ─── Internal ──────────────────────────────────────────────
154
+ _detectSupport() {
155
+ if (typeof window === "undefined") return false;
156
+ const hasAudioContext = typeof AudioContext !== "undefined" || typeof window.webkitAudioContext !== "undefined";
157
+ if (!hasAudioContext) return false;
158
+ const ua = navigator.userAgent;
159
+ const isIOS = /iPad|iPhone|iPod/.test(ua) || navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1;
160
+ return isIOS;
161
+ }
162
+ /** Get or create the AudioContext, resuming if suspended */
163
+ async _getAudioContext() {
164
+ if (!this._audioCtx) {
165
+ const Ctor = typeof AudioContext !== "undefined" ? AudioContext : window.webkitAudioContext;
166
+ this._audioCtx = new Ctor();
167
+ }
168
+ if (this._audioCtx.state === "suspended") {
169
+ await this._audioCtx.resume();
170
+ }
171
+ return this._audioCtx;
172
+ }
173
+ /** Map intensity (0-1) to frequency in the 20-60 Hz sub-bass range */
174
+ _intensityToFrequency(intensity) {
175
+ return 20 + intensity * 40;
176
+ }
177
+ /** Play a single oscillator tone for the given duration */
178
+ async _playTone(intensity, duration) {
179
+ const ctx = await this._getAudioContext();
180
+ const oscillator = ctx.createOscillator();
181
+ const gainNode = ctx.createGain();
182
+ oscillator.type = "sine";
183
+ oscillator.frequency.setValueAtTime(
184
+ this._intensityToFrequency(intensity),
185
+ ctx.currentTime
115
186
  );
116
- }
117
- /** Simulate lower intensity via pulse-width modulation */
118
- async _pwmVibrate(duration, intensity) {
119
- const cycleTime = 20;
120
- const onTime = Math.round(cycleTime * intensity);
121
- const offTime = cycleTime - onTime;
122
- const cycles = Math.floor(duration / cycleTime);
123
- const pattern = [];
124
- for (let i = 0; i < cycles; i++) {
125
- pattern.push(onTime, offTime);
187
+ gainNode.gain.setValueAtTime(intensity, ctx.currentTime);
188
+ oscillator.connect(gainNode);
189
+ gainNode.connect(ctx.destination);
190
+ this._activeOscillator = oscillator;
191
+ this._activeGain = gainNode;
192
+ oscillator.start(ctx.currentTime);
193
+ oscillator.stop(ctx.currentTime + duration / 1e3);
194
+ await new Promise((resolve) => {
195
+ oscillator.onended = () => {
196
+ oscillator.disconnect();
197
+ gainNode.disconnect();
198
+ this._activeOscillator = null;
199
+ this._activeGain = null;
200
+ resolve();
201
+ };
202
+ });
203
+ }
204
+ /** Immediately stop the active oscillator if any */
205
+ _stopOscillator() {
206
+ if (this._activeOscillator) {
207
+ try {
208
+ this._activeOscillator.stop();
209
+ this._activeOscillator.disconnect();
210
+ } catch {
211
+ }
212
+ this._activeOscillator = null;
126
213
  }
127
- if (pattern.length > 0) {
128
- navigator.vibrate(pattern);
129
- await delay(duration);
214
+ if (this._activeGain) {
215
+ try {
216
+ this._activeGain.disconnect();
217
+ } catch {
218
+ }
219
+ this._activeGain = null;
130
220
  }
131
221
  }
132
222
  };
@@ -160,6 +250,12 @@ function detectAdapter() {
160
250
  if (platform.hasVibrationAPI) {
161
251
  return new WebVibrationAdapter();
162
252
  }
253
+ if (platform.isIOS && platform.isWeb) {
254
+ const iosAdapter = new IoSAudioAdapter();
255
+ if (iosAdapter.supported) {
256
+ return iosAdapter;
257
+ }
258
+ }
163
259
  return new NoopAdapter();
164
260
  }
165
261
 
@@ -194,13 +290,13 @@ var FallbackManager = class {
194
290
  /** Execute fallback feedback for the given steps */
195
291
  async execute(steps) {
196
292
  if (this.config.type === "none") return;
197
- const totalDuration = steps.reduce((sum, s) => sum + s.duration, 0);
293
+ const totalDuration2 = steps.reduce((sum, s) => sum + s.duration, 0);
198
294
  const maxIntensity = Math.max(...steps.filter((s) => s.type === "vibrate").map((s) => s.intensity), 0);
199
295
  if (this.config.type === "visual" || this.config.type === "both") {
200
- await this._visualFallback(totalDuration, maxIntensity);
296
+ await this._visualFallback(totalDuration2, maxIntensity);
201
297
  }
202
298
  if (this.config.type === "audio" || this.config.type === "both") {
203
- await this._audioFallback(totalDuration, maxIntensity);
299
+ await this._audioFallback(totalDuration2, maxIntensity);
204
300
  }
205
301
  }
206
302
  async _visualFallback(duration, intensity) {
@@ -605,14 +701,14 @@ var HapticEngine = class _HapticEngine {
605
701
  // ─── Semantic API ──────────────────────────────────────────
606
702
  /** Light tap feedback */
607
703
  async tap(intensity = 0.6) {
608
- await this._playSteps([{ type: "vibrate", duration: 10, intensity }]);
704
+ await this._playSteps([{ type: "vibrate", duration: 30, intensity }]);
609
705
  }
610
706
  /** Double tap */
611
707
  async doubleTap(intensity = 0.6) {
612
708
  await this._playSteps([
613
- { type: "vibrate", duration: 10, intensity },
709
+ { type: "vibrate", duration: 25, intensity },
614
710
  { type: "pause", duration: 80, intensity: 0 },
615
- { type: "vibrate", duration: 10, intensity }
711
+ { type: "vibrate", duration: 25, intensity }
616
712
  ]);
617
713
  }
618
714
  /** Long press feedback */
@@ -647,24 +743,24 @@ var HapticEngine = class _HapticEngine {
647
743
  }
648
744
  /** Selection change feedback */
649
745
  async selection() {
650
- await this._playSteps([{ type: "vibrate", duration: 8, intensity: 0.4 }]);
746
+ await this._playSteps([{ type: "vibrate", duration: 25, intensity: 0.5 }]);
651
747
  }
652
748
  /** Toggle feedback */
653
749
  async toggle(on) {
654
750
  if (on) {
655
- await this._playSteps([{ type: "vibrate", duration: 15, intensity: 0.6 }]);
751
+ await this._playSteps([{ type: "vibrate", duration: 30, intensity: 0.6 }]);
656
752
  } else {
657
- await this._playSteps([{ type: "vibrate", duration: 10, intensity: 0.3 }]);
753
+ await this._playSteps([{ type: "vibrate", duration: 25, intensity: 0.4 }]);
658
754
  }
659
755
  }
660
756
  /** Impact with style (matches iOS UIImpactFeedbackGenerator) */
661
757
  async impact(style = "medium") {
662
758
  const presets2 = {
663
- light: [{ type: "vibrate", duration: 10, intensity: 0.3 }],
664
- medium: [{ type: "vibrate", duration: 15, intensity: 0.6 }],
665
- heavy: [{ type: "vibrate", duration: 25, intensity: 1 }],
666
- rigid: [{ type: "vibrate", duration: 8, intensity: 0.9 }],
667
- soft: [{ type: "vibrate", duration: 30, intensity: 0.4 }]
759
+ light: [{ type: "vibrate", duration: 25, intensity: 0.4 }],
760
+ medium: [{ type: "vibrate", duration: 35, intensity: 0.7 }],
761
+ heavy: [{ type: "vibrate", duration: 50, intensity: 1 }],
762
+ rigid: [{ type: "vibrate", duration: 30, intensity: 0.9 }],
763
+ soft: [{ type: "vibrate", duration: 35, intensity: 0.5 }]
668
764
  };
669
765
  await this._playSteps(presets2[style]);
670
766
  }
@@ -785,20 +881,135 @@ function validateHPL(input) {
785
881
  return { valid: errors.length === 0, errors };
786
882
  }
787
883
 
884
+ // src/patterns/sharing.ts
885
+ function totalDuration(steps) {
886
+ return steps.reduce((sum, s) => sum + s.duration, 0);
887
+ }
888
+ function resolveInput(input) {
889
+ if (typeof input === "string") {
890
+ const ast = parseHPL(input);
891
+ return { steps: compile(ast), hpl: input };
892
+ }
893
+ if (Array.isArray(input)) {
894
+ return { steps: input };
895
+ }
896
+ return { steps: input.steps, name: input.name };
897
+ }
898
+ function exportPattern(input, options = {}) {
899
+ const resolved = resolveInput(input);
900
+ const steps = resolved.steps;
901
+ const name = options.name ?? resolved.name ?? "Untitled Pattern";
902
+ const result = {
903
+ version: 1,
904
+ name,
905
+ steps
906
+ };
907
+ if (options.description) {
908
+ result.description = options.description;
909
+ }
910
+ if (resolved.hpl) {
911
+ result.hpl = resolved.hpl;
912
+ }
913
+ const hasMetadataFields = options.author || options.tags;
914
+ if (hasMetadataFields || steps.length > 0) {
915
+ result.metadata = {
916
+ duration: totalDuration(steps),
917
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
918
+ };
919
+ if (options.author) {
920
+ result.metadata.author = options.author;
921
+ }
922
+ if (options.tags) {
923
+ result.metadata.tags = options.tags;
924
+ }
925
+ }
926
+ return result;
927
+ }
928
+ function validateExport(data) {
929
+ if (data === null || typeof data !== "object") {
930
+ throw new Error("Invalid pattern data: expected an object");
931
+ }
932
+ const obj = data;
933
+ if (obj.version !== 1) {
934
+ throw new Error(
935
+ `Unsupported pattern version: ${String(obj.version)}. Expected version 1`
936
+ );
937
+ }
938
+ if (typeof obj.name !== "string" || obj.name.length === 0) {
939
+ throw new Error('Invalid pattern data: "name" must be a non-empty string');
940
+ }
941
+ if (!Array.isArray(obj.steps)) {
942
+ throw new Error('Invalid pattern data: "steps" must be an array');
943
+ }
944
+ for (let i = 0; i < obj.steps.length; i++) {
945
+ const step = obj.steps[i];
946
+ if (step.type !== "vibrate" && step.type !== "pause") {
947
+ throw new Error(
948
+ `Invalid step at index ${i}: "type" must be "vibrate" or "pause"`
949
+ );
950
+ }
951
+ if (typeof step.duration !== "number" || step.duration < 0) {
952
+ throw new Error(
953
+ `Invalid step at index ${i}: "duration" must be a non-negative number`
954
+ );
955
+ }
956
+ if (typeof step.intensity !== "number" || step.intensity < 0 || step.intensity > 1) {
957
+ throw new Error(
958
+ `Invalid step at index ${i}: "intensity" must be a number between 0 and 1`
959
+ );
960
+ }
961
+ }
962
+ }
963
+ function importPattern(data) {
964
+ const parsed = typeof data === "string" ? JSON.parse(data) : data;
965
+ validateExport(parsed);
966
+ const pattern = {
967
+ name: parsed.name,
968
+ steps: parsed.steps.map((s) => ({ ...s }))
969
+ };
970
+ if (parsed.metadata) {
971
+ pattern.metadata = { ...parsed.metadata };
972
+ }
973
+ return pattern;
974
+ }
975
+ function patternToJSON(input, options = {}) {
976
+ const exported = exportPattern(input, options);
977
+ return JSON.stringify(exported, null, 2);
978
+ }
979
+ function patternFromJSON(json) {
980
+ return importPattern(json);
981
+ }
982
+ function patternToDataURL(input, options = {}) {
983
+ const json = patternToJSON(input, options);
984
+ const encoded = btoa(json);
985
+ return `data:application/haptic+json;base64,${encoded}`;
986
+ }
987
+ function patternFromDataURL(url) {
988
+ const prefix = "data:application/haptic+json;base64,";
989
+ if (!url.startsWith(prefix)) {
990
+ throw new Error(
991
+ 'Invalid haptic data URL: expected "data:application/haptic+json;base64," prefix'
992
+ );
993
+ }
994
+ const encoded = url.slice(prefix.length);
995
+ const json = atob(encoded);
996
+ return patternFromJSON(json);
997
+ }
998
+
788
999
  // src/presets/ui.ts
789
1000
  var ui = {
790
1001
  /** Light button tap */
791
1002
  tap: {
792
1003
  name: "ui.tap",
793
- steps: [{ type: "vibrate", duration: 10, intensity: 0.6 }]
1004
+ steps: [{ type: "vibrate", duration: 30, intensity: 0.6 }]
794
1005
  },
795
1006
  /** Double tap */
796
1007
  doubleTap: {
797
1008
  name: "ui.doubleTap",
798
1009
  steps: [
799
- { type: "vibrate", duration: 10, intensity: 0.6 },
1010
+ { type: "vibrate", duration: 25, intensity: 0.6 },
800
1011
  { type: "pause", duration: 80, intensity: 0 },
801
- { type: "vibrate", duration: 10, intensity: 0.6 }
1012
+ { type: "vibrate", duration: 25, intensity: 0.6 }
802
1013
  ]
803
1014
  },
804
1015
  /** Long press acknowledgment */
@@ -809,58 +1020,58 @@ var ui = {
809
1020
  /** Toggle switch on */
810
1021
  toggleOn: {
811
1022
  name: "ui.toggleOn",
812
- steps: [{ type: "vibrate", duration: 15, intensity: 0.6 }]
1023
+ steps: [{ type: "vibrate", duration: 30, intensity: 0.6 }]
813
1024
  },
814
1025
  /** Toggle switch off */
815
1026
  toggleOff: {
816
1027
  name: "ui.toggleOff",
817
- steps: [{ type: "vibrate", duration: 10, intensity: 0.3 }]
1028
+ steps: [{ type: "vibrate", duration: 25, intensity: 0.4 }]
818
1029
  },
819
1030
  /** Slider snap to value */
820
1031
  sliderSnap: {
821
1032
  name: "ui.sliderSnap",
822
- steps: [{ type: "vibrate", duration: 5, intensity: 0.4 }]
1033
+ steps: [{ type: "vibrate", duration: 25, intensity: 0.5 }]
823
1034
  },
824
1035
  /** Selection changed */
825
1036
  selection: {
826
1037
  name: "ui.selection",
827
- steps: [{ type: "vibrate", duration: 8, intensity: 0.4 }]
1038
+ steps: [{ type: "vibrate", duration: 25, intensity: 0.5 }]
828
1039
  },
829
1040
  /** Pull to refresh threshold reached */
830
1041
  pullToRefresh: {
831
1042
  name: "ui.pullToRefresh",
832
1043
  steps: [
833
- { type: "vibrate", duration: 20, intensity: 0.5 },
1044
+ { type: "vibrate", duration: 30, intensity: 0.5 },
834
1045
  { type: "pause", duration: 40, intensity: 0 },
835
- { type: "vibrate", duration: 30, intensity: 0.7 }
1046
+ { type: "vibrate", duration: 40, intensity: 0.7 }
836
1047
  ]
837
1048
  },
838
1049
  /** Swipe action triggered */
839
1050
  swipe: {
840
1051
  name: "ui.swipe",
841
1052
  steps: [
842
- { type: "vibrate", duration: 12, intensity: 0.4 },
1053
+ { type: "vibrate", duration: 30, intensity: 0.5 },
843
1054
  { type: "pause", duration: 30, intensity: 0 },
844
- { type: "vibrate", duration: 8, intensity: 0.3 }
1055
+ { type: "vibrate", duration: 25, intensity: 0.4 }
845
1056
  ]
846
1057
  },
847
1058
  /** Context menu appearance */
848
1059
  contextMenu: {
849
1060
  name: "ui.contextMenu",
850
- steps: [{ type: "vibrate", duration: 20, intensity: 0.7 }]
1061
+ steps: [{ type: "vibrate", duration: 35, intensity: 0.7 }]
851
1062
  },
852
1063
  /** Drag start */
853
1064
  dragStart: {
854
1065
  name: "ui.dragStart",
855
- steps: [{ type: "vibrate", duration: 12, intensity: 0.5 }]
1066
+ steps: [{ type: "vibrate", duration: 30, intensity: 0.5 }]
856
1067
  },
857
1068
  /** Drag drop */
858
1069
  drop: {
859
1070
  name: "ui.drop",
860
1071
  steps: [
861
- { type: "vibrate", duration: 20, intensity: 0.8 },
1072
+ { type: "vibrate", duration: 30, intensity: 0.8 },
862
1073
  { type: "pause", duration: 30, intensity: 0 },
863
- { type: "vibrate", duration: 10, intensity: 0.4 }
1074
+ { type: "vibrate", duration: 25, intensity: 0.5 }
864
1075
  ]
865
1076
  }
866
1077
  };
@@ -871,9 +1082,9 @@ var notifications = {
871
1082
  success: {
872
1083
  name: "notifications.success",
873
1084
  steps: [
874
- { type: "vibrate", duration: 30, intensity: 0.5 },
1085
+ { type: "vibrate", duration: 35, intensity: 0.5 },
875
1086
  { type: "pause", duration: 60, intensity: 0 },
876
- { type: "vibrate", duration: 40, intensity: 0.8 }
1087
+ { type: "vibrate", duration: 45, intensity: 0.8 }
877
1088
  ]
878
1089
  },
879
1090
  /** Warning — three even pulses */
@@ -900,16 +1111,16 @@ var notifications = {
900
1111
  info: {
901
1112
  name: "notifications.info",
902
1113
  steps: [
903
- { type: "vibrate", duration: 20, intensity: 0.4 }
1114
+ { type: "vibrate", duration: 35, intensity: 0.5 }
904
1115
  ]
905
1116
  },
906
1117
  /** Message received */
907
1118
  messageReceived: {
908
1119
  name: "notifications.messageReceived",
909
1120
  steps: [
910
- { type: "vibrate", duration: 15, intensity: 0.5 },
1121
+ { type: "vibrate", duration: 30, intensity: 0.5 },
911
1122
  { type: "pause", duration: 100, intensity: 0 },
912
- { type: "vibrate", duration: 15, intensity: 0.5 }
1123
+ { type: "vibrate", duration: 30, intensity: 0.5 }
913
1124
  ]
914
1125
  },
915
1126
  /** Alarm — urgent repeating pattern */
@@ -933,9 +1144,9 @@ var notifications = {
933
1144
  reminder: {
934
1145
  name: "notifications.reminder",
935
1146
  steps: [
936
- { type: "vibrate", duration: 25, intensity: 0.5 },
1147
+ { type: "vibrate", duration: 30, intensity: 0.5 },
937
1148
  { type: "pause", duration: 150, intensity: 0 },
938
- { type: "vibrate", duration: 25, intensity: 0.5 }
1149
+ { type: "vibrate", duration: 30, intensity: 0.5 }
939
1150
  ]
940
1151
  }
941
1152
  };
@@ -949,26 +1160,25 @@ var gaming = {
949
1160
  { type: "vibrate", duration: 100, intensity: 1 },
950
1161
  { type: "vibrate", duration: 80, intensity: 0.8 },
951
1162
  { type: "vibrate", duration: 60, intensity: 0.5 },
952
- { type: "vibrate", duration: 40, intensity: 0.3 },
953
- { type: "vibrate", duration: 30, intensity: 0.1 }
1163
+ { type: "vibrate", duration: 40, intensity: 0.3 }
954
1164
  ]
955
1165
  },
956
1166
  /** Collision — sharp impact */
957
1167
  collision: {
958
1168
  name: "gaming.collision",
959
1169
  steps: [
960
- { type: "vibrate", duration: 30, intensity: 1 },
961
- { type: "pause", duration: 20, intensity: 0 },
962
- { type: "vibrate", duration: 15, intensity: 0.5 }
1170
+ { type: "vibrate", duration: 40, intensity: 1 },
1171
+ { type: "pause", duration: 30, intensity: 0 },
1172
+ { type: "vibrate", duration: 25, intensity: 0.5 }
963
1173
  ]
964
1174
  },
965
1175
  /** Heartbeat — rhythmic pulse */
966
1176
  heartbeat: {
967
1177
  name: "gaming.heartbeat",
968
1178
  steps: [
969
- { type: "vibrate", duration: 20, intensity: 0.8 },
1179
+ { type: "vibrate", duration: 30, intensity: 0.8 },
970
1180
  { type: "pause", duration: 80, intensity: 0 },
971
- { type: "vibrate", duration: 30, intensity: 1 },
1181
+ { type: "vibrate", duration: 40, intensity: 1 },
972
1182
  { type: "pause", duration: 400, intensity: 0 }
973
1183
  ]
974
1184
  },
@@ -976,17 +1186,17 @@ var gaming = {
976
1186
  gunshot: {
977
1187
  name: "gaming.gunshot",
978
1188
  steps: [
979
- { type: "vibrate", duration: 15, intensity: 1 },
980
- { type: "vibrate", duration: 30, intensity: 0.4 }
1189
+ { type: "vibrate", duration: 30, intensity: 1 },
1190
+ { type: "vibrate", duration: 40, intensity: 0.4 }
981
1191
  ]
982
1192
  },
983
1193
  /** Sword clash — metallic ring */
984
1194
  swordClash: {
985
1195
  name: "gaming.swordClash",
986
1196
  steps: [
987
- { type: "vibrate", duration: 10, intensity: 1 },
988
- { type: "pause", duration: 10, intensity: 0 },
989
- { type: "vibrate", duration: 30, intensity: 0.6 },
1197
+ { type: "vibrate", duration: 25, intensity: 1 },
1198
+ { type: "pause", duration: 20, intensity: 0 },
1199
+ { type: "vibrate", duration: 40, intensity: 0.6 },
990
1200
  { type: "vibrate", duration: 50, intensity: 0.3 }
991
1201
  ]
992
1202
  },
@@ -994,10 +1204,10 @@ var gaming = {
994
1204
  powerUp: {
995
1205
  name: "gaming.powerUp",
996
1206
  steps: [
997
- { type: "vibrate", duration: 40, intensity: 0.2 },
998
- { type: "vibrate", duration: 40, intensity: 0.4 },
999
- { type: "vibrate", duration: 40, intensity: 0.6 },
1000
- { type: "vibrate", duration: 40, intensity: 0.8 },
1207
+ { type: "vibrate", duration: 40, intensity: 0.3 },
1208
+ { type: "vibrate", duration: 40, intensity: 0.5 },
1209
+ { type: "vibrate", duration: 40, intensity: 0.7 },
1210
+ { type: "vibrate", duration: 40, intensity: 0.9 },
1001
1211
  { type: "vibrate", duration: 60, intensity: 1 }
1002
1212
  ]
1003
1213
  },
@@ -1005,46 +1215,46 @@ var gaming = {
1005
1215
  damage: {
1006
1216
  name: "gaming.damage",
1007
1217
  steps: [
1008
- { type: "vibrate", duration: 40, intensity: 0.9 },
1009
- { type: "pause", duration: 20, intensity: 0 },
1010
- { type: "vibrate", duration: 30, intensity: 0.6 },
1011
- { type: "pause", duration: 20, intensity: 0 },
1012
- { type: "vibrate", duration: 20, intensity: 0.3 }
1218
+ { type: "vibrate", duration: 50, intensity: 0.9 },
1219
+ { type: "pause", duration: 25, intensity: 0 },
1220
+ { type: "vibrate", duration: 40, intensity: 0.6 },
1221
+ { type: "pause", duration: 25, intensity: 0 },
1222
+ { type: "vibrate", duration: 30, intensity: 0.4 }
1013
1223
  ]
1014
1224
  },
1015
1225
  /** Item pickup — light cheerful */
1016
1226
  pickup: {
1017
1227
  name: "gaming.pickup",
1018
1228
  steps: [
1019
- { type: "vibrate", duration: 10, intensity: 0.3 },
1229
+ { type: "vibrate", duration: 25, intensity: 0.4 },
1020
1230
  { type: "pause", duration: 40, intensity: 0 },
1021
- { type: "vibrate", duration: 15, intensity: 0.6 }
1231
+ { type: "vibrate", duration: 30, intensity: 0.7 }
1022
1232
  ]
1023
1233
  },
1024
1234
  /** Level complete — celebratory */
1025
1235
  levelComplete: {
1026
1236
  name: "gaming.levelComplete",
1027
1237
  steps: [
1028
- { type: "vibrate", duration: 20, intensity: 0.5 },
1238
+ { type: "vibrate", duration: 30, intensity: 0.5 },
1029
1239
  { type: "pause", duration: 60, intensity: 0 },
1030
- { type: "vibrate", duration: 20, intensity: 0.5 },
1240
+ { type: "vibrate", duration: 30, intensity: 0.5 },
1031
1241
  { type: "pause", duration: 60, intensity: 0 },
1032
- { type: "vibrate", duration: 30, intensity: 0.7 },
1242
+ { type: "vibrate", duration: 40, intensity: 0.7 },
1033
1243
  { type: "pause", duration: 60, intensity: 0 },
1034
- { type: "vibrate", duration: 50, intensity: 1 }
1244
+ { type: "vibrate", duration: 60, intensity: 1 }
1035
1245
  ]
1036
1246
  },
1037
1247
  /** Engine rumble — continuous vibration */
1038
1248
  engineRumble: {
1039
1249
  name: "gaming.engineRumble",
1040
1250
  steps: [
1041
- { type: "vibrate", duration: 30, intensity: 0.4 },
1042
- { type: "pause", duration: 10, intensity: 0 },
1043
- { type: "vibrate", duration: 30, intensity: 0.5 },
1044
- { type: "pause", duration: 10, intensity: 0 },
1045
- { type: "vibrate", duration: 30, intensity: 0.4 },
1046
- { type: "pause", duration: 10, intensity: 0 },
1047
- { type: "vibrate", duration: 30, intensity: 0.5 }
1251
+ { type: "vibrate", duration: 40, intensity: 0.5 },
1252
+ { type: "pause", duration: 15, intensity: 0 },
1253
+ { type: "vibrate", duration: 40, intensity: 0.6 },
1254
+ { type: "pause", duration: 15, intensity: 0 },
1255
+ { type: "vibrate", duration: 40, intensity: 0.5 },
1256
+ { type: "pause", duration: 15, intensity: 0 },
1257
+ { type: "vibrate", duration: 40, intensity: 0.6 }
1048
1258
  ]
1049
1259
  }
1050
1260
  };
@@ -1055,9 +1265,9 @@ var accessibility = {
1055
1265
  confirm: {
1056
1266
  name: "accessibility.confirm",
1057
1267
  steps: [
1058
- { type: "vibrate", duration: 30, intensity: 0.7 },
1268
+ { type: "vibrate", duration: 35, intensity: 0.7 },
1059
1269
  { type: "pause", duration: 100, intensity: 0 },
1060
- { type: "vibrate", duration: 30, intensity: 0.7 }
1270
+ { type: "vibrate", duration: 35, intensity: 0.7 }
1061
1271
  ]
1062
1272
  },
1063
1273
  /** Deny/reject — long single buzz */
@@ -1071,41 +1281,41 @@ var accessibility = {
1071
1281
  boundary: {
1072
1282
  name: "accessibility.boundary",
1073
1283
  steps: [
1074
- { type: "vibrate", duration: 15, intensity: 1 }
1284
+ { type: "vibrate", duration: 30, intensity: 1 }
1075
1285
  ]
1076
1286
  },
1077
1287
  /** Focus change — subtle tick */
1078
1288
  focusChange: {
1079
1289
  name: "accessibility.focusChange",
1080
1290
  steps: [
1081
- { type: "vibrate", duration: 5, intensity: 0.3 }
1291
+ { type: "vibrate", duration: 25, intensity: 0.5 }
1082
1292
  ]
1083
1293
  },
1084
1294
  /** Counting rhythm — one tick per count */
1085
1295
  countTick: {
1086
1296
  name: "accessibility.countTick",
1087
1297
  steps: [
1088
- { type: "vibrate", duration: 8, intensity: 0.5 }
1298
+ { type: "vibrate", duration: 25, intensity: 0.5 }
1089
1299
  ]
1090
1300
  },
1091
1301
  /** Navigation landmark reached */
1092
1302
  landmark: {
1093
1303
  name: "accessibility.landmark",
1094
1304
  steps: [
1095
- { type: "vibrate", duration: 15, intensity: 0.6 },
1305
+ { type: "vibrate", duration: 25, intensity: 0.6 },
1096
1306
  { type: "pause", duration: 40, intensity: 0 },
1097
- { type: "vibrate", duration: 15, intensity: 0.6 },
1307
+ { type: "vibrate", duration: 25, intensity: 0.6 },
1098
1308
  { type: "pause", duration: 40, intensity: 0 },
1099
- { type: "vibrate", duration: 15, intensity: 0.6 }
1309
+ { type: "vibrate", duration: 25, intensity: 0.6 }
1100
1310
  ]
1101
1311
  },
1102
1312
  /** Progress checkpoint — escalating feedback */
1103
1313
  progressCheckpoint: {
1104
1314
  name: "accessibility.progressCheckpoint",
1105
1315
  steps: [
1106
- { type: "vibrate", duration: 20, intensity: 0.4 },
1316
+ { type: "vibrate", duration: 30, intensity: 0.5 },
1107
1317
  { type: "pause", duration: 60, intensity: 0 },
1108
- { type: "vibrate", duration: 25, intensity: 0.7 }
1318
+ { type: "vibrate", duration: 35, intensity: 0.7 }
1109
1319
  ]
1110
1320
  }
1111
1321
  };
@@ -1115,51 +1325,51 @@ var system = {
1115
1325
  /** Keyboard key press */
1116
1326
  keyPress: {
1117
1327
  name: "system.keyPress",
1118
- steps: [{ type: "vibrate", duration: 5, intensity: 0.3 }]
1328
+ steps: [{ type: "vibrate", duration: 25, intensity: 0.5 }]
1119
1329
  },
1120
1330
  /** Scroll tick (detent-like) */
1121
1331
  scrollTick: {
1122
1332
  name: "system.scrollTick",
1123
- steps: [{ type: "vibrate", duration: 3, intensity: 0.2 }]
1333
+ steps: [{ type: "vibrate", duration: 20, intensity: 0.4 }]
1124
1334
  },
1125
1335
  /** Scroll boundary reached */
1126
1336
  scrollBounce: {
1127
1337
  name: "system.scrollBounce",
1128
1338
  steps: [
1129
- { type: "vibrate", duration: 10, intensity: 0.5 },
1130
- { type: "vibrate", duration: 20, intensity: 0.3 }
1339
+ { type: "vibrate", duration: 25, intensity: 0.6 },
1340
+ { type: "vibrate", duration: 30, intensity: 0.4 }
1131
1341
  ]
1132
1342
  },
1133
1343
  /** Delete action */
1134
1344
  delete: {
1135
1345
  name: "system.delete",
1136
1346
  steps: [
1137
- { type: "vibrate", duration: 15, intensity: 0.5 },
1347
+ { type: "vibrate", duration: 30, intensity: 0.5 },
1138
1348
  { type: "pause", duration: 50, intensity: 0 },
1139
- { type: "vibrate", duration: 25, intensity: 0.8 }
1349
+ { type: "vibrate", duration: 40, intensity: 0.8 }
1140
1350
  ]
1141
1351
  },
1142
1352
  /** Undo action */
1143
1353
  undo: {
1144
1354
  name: "system.undo",
1145
1355
  steps: [
1146
- { type: "vibrate", duration: 20, intensity: 0.5 },
1356
+ { type: "vibrate", duration: 30, intensity: 0.5 },
1147
1357
  { type: "pause", duration: 80, intensity: 0 },
1148
- { type: "vibrate", duration: 10, intensity: 0.3 }
1358
+ { type: "vibrate", duration: 25, intensity: 0.4 }
1149
1359
  ]
1150
1360
  },
1151
1361
  /** Copy to clipboard */
1152
1362
  copy: {
1153
1363
  name: "system.copy",
1154
- steps: [{ type: "vibrate", duration: 12, intensity: 0.4 }]
1364
+ steps: [{ type: "vibrate", duration: 30, intensity: 0.5 }]
1155
1365
  },
1156
1366
  /** Paste from clipboard */
1157
1367
  paste: {
1158
1368
  name: "system.paste",
1159
1369
  steps: [
1160
- { type: "vibrate", duration: 8, intensity: 0.3 },
1370
+ { type: "vibrate", duration: 25, intensity: 0.4 },
1161
1371
  { type: "pause", duration: 30, intensity: 0 },
1162
- { type: "vibrate", duration: 12, intensity: 0.5 }
1372
+ { type: "vibrate", duration: 30, intensity: 0.6 }
1163
1373
  ]
1164
1374
  }
1165
1375
  };
@@ -1176,6 +1386,6 @@ var presets = {
1176
1386
  // src/index.ts
1177
1387
  var haptic = HapticEngine.create();
1178
1388
 
1179
- export { AdaptiveEngine, FallbackManager, HPLParser, HPLParserError, HPLTokenizerError, HapticEngine, NoopAdapter, PatternComposer, WebVibrationAdapter, accessibility, compile, detectAdapter, detectPlatform, gaming, haptic, notifications, optimizeSteps, parseHPL, presets, system, tokenize, ui, validateHPL };
1389
+ export { AdaptiveEngine, FallbackManager, HPLParser, HPLParserError, HPLTokenizerError, HapticEngine, IoSAudioAdapter, NoopAdapter, PatternComposer, WebVibrationAdapter, accessibility, compile, detectAdapter, detectPlatform, exportPattern, gaming, haptic, importPattern, notifications, optimizeSteps, parseHPL, patternFromDataURL, patternFromJSON, patternToDataURL, patternToJSON, presets, system, tokenize, ui, validateHPL };
1180
1390
  //# sourceMappingURL=index.js.map
1181
1391
  //# sourceMappingURL=index.js.map