@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/LICENSE +21 -0
- package/README.md +66 -0
- package/dist/index.cjs +362 -145
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +88 -9
- package/dist/index.d.ts +88 -9
- package/dist/index.js +356 -146
- package/dist/index.js.map +1 -1
- package/dist/presets/index.cjs +76 -77
- package/dist/presets/index.cjs.map +1 -1
- package/dist/presets/index.js +76 -77
- package/dist/presets/index.js.map +1 -1
- package/package.json +3 -2
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/
|
|
47
|
-
var
|
|
106
|
+
// src/adapters/ios-audio.adapter.ts
|
|
107
|
+
var IoSAudioAdapter = class {
|
|
48
108
|
constructor() {
|
|
49
|
-
this.name = "
|
|
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 =
|
|
114
|
+
this.supported = this._detectSupport();
|
|
52
115
|
}
|
|
53
116
|
capabilities() {
|
|
54
117
|
return {
|
|
55
|
-
maxIntensityLevels:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
maxDuration: 1e4,
|
|
118
|
+
maxIntensityLevels: 100,
|
|
119
|
+
minDuration: 5,
|
|
120
|
+
maxDuration: 5e3,
|
|
59
121
|
supportsPattern: true,
|
|
60
|
-
supportsIntensity:
|
|
122
|
+
supportsIntensity: true,
|
|
61
123
|
dualMotor: false
|
|
62
124
|
};
|
|
63
125
|
}
|
|
64
|
-
async pulse(
|
|
126
|
+
async pulse(intensity, duration) {
|
|
65
127
|
if (!this.supported) return;
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
|
|
97
|
-
navigator.vibrate(0);
|
|
98
|
-
}
|
|
144
|
+
this._stopOscillator();
|
|
99
145
|
}
|
|
100
146
|
dispose() {
|
|
101
147
|
this.cancel();
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
return
|
|
114
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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 (
|
|
128
|
-
|
|
129
|
-
|
|
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
|
|
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(
|
|
296
|
+
await this._visualFallback(totalDuration2, maxIntensity);
|
|
201
297
|
}
|
|
202
298
|
if (this.config.type === "audio" || this.config.type === "both") {
|
|
203
|
-
await this._audioFallback(
|
|
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:
|
|
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:
|
|
709
|
+
{ type: "vibrate", duration: 25, intensity },
|
|
614
710
|
{ type: "pause", duration: 80, intensity: 0 },
|
|
615
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
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:
|
|
751
|
+
await this._playSteps([{ type: "vibrate", duration: 30, intensity: 0.6 }]);
|
|
656
752
|
} else {
|
|
657
|
-
await this._playSteps([{ type: "vibrate", duration:
|
|
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:
|
|
664
|
-
medium: [{ type: "vibrate", duration:
|
|
665
|
-
heavy: [{ type: "vibrate", duration:
|
|
666
|
-
rigid: [{ type: "vibrate", duration:
|
|
667
|
-
soft: [{ type: "vibrate", duration:
|
|
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:
|
|
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:
|
|
1010
|
+
{ type: "vibrate", duration: 25, intensity: 0.6 },
|
|
800
1011
|
{ type: "pause", duration: 80, intensity: 0 },
|
|
801
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
1044
|
+
{ type: "vibrate", duration: 30, intensity: 0.5 },
|
|
834
1045
|
{ type: "pause", duration: 40, intensity: 0 },
|
|
835
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
1053
|
+
{ type: "vibrate", duration: 30, intensity: 0.5 },
|
|
843
1054
|
{ type: "pause", duration: 30, intensity: 0 },
|
|
844
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
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:
|
|
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:
|
|
1072
|
+
{ type: "vibrate", duration: 30, intensity: 0.8 },
|
|
862
1073
|
{ type: "pause", duration: 30, intensity: 0 },
|
|
863
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
1085
|
+
{ type: "vibrate", duration: 35, intensity: 0.5 },
|
|
875
1086
|
{ type: "pause", duration: 60, intensity: 0 },
|
|
876
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
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:
|
|
1121
|
+
{ type: "vibrate", duration: 30, intensity: 0.5 },
|
|
911
1122
|
{ type: "pause", duration: 100, intensity: 0 },
|
|
912
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
1147
|
+
{ type: "vibrate", duration: 30, intensity: 0.5 },
|
|
937
1148
|
{ type: "pause", duration: 150, intensity: 0 },
|
|
938
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
961
|
-
{ type: "pause", duration:
|
|
962
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
1179
|
+
{ type: "vibrate", duration: 30, intensity: 0.8 },
|
|
970
1180
|
{ type: "pause", duration: 80, intensity: 0 },
|
|
971
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
980
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
988
|
-
{ type: "pause", duration:
|
|
989
|
-
{ type: "vibrate", duration:
|
|
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.
|
|
998
|
-
{ type: "vibrate", duration: 40, intensity: 0.
|
|
999
|
-
{ type: "vibrate", duration: 40, intensity: 0.
|
|
1000
|
-
{ type: "vibrate", duration: 40, intensity: 0.
|
|
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:
|
|
1009
|
-
{ type: "pause", duration:
|
|
1010
|
-
{ type: "vibrate", duration:
|
|
1011
|
-
{ type: "pause", duration:
|
|
1012
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
1229
|
+
{ type: "vibrate", duration: 25, intensity: 0.4 },
|
|
1020
1230
|
{ type: "pause", duration: 40, intensity: 0 },
|
|
1021
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
1238
|
+
{ type: "vibrate", duration: 30, intensity: 0.5 },
|
|
1029
1239
|
{ type: "pause", duration: 60, intensity: 0 },
|
|
1030
|
-
{ type: "vibrate", duration:
|
|
1240
|
+
{ type: "vibrate", duration: 30, intensity: 0.5 },
|
|
1031
1241
|
{ type: "pause", duration: 60, intensity: 0 },
|
|
1032
|
-
{ type: "vibrate", duration:
|
|
1242
|
+
{ type: "vibrate", duration: 40, intensity: 0.7 },
|
|
1033
1243
|
{ type: "pause", duration: 60, intensity: 0 },
|
|
1034
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
1042
|
-
{ type: "pause", duration:
|
|
1043
|
-
{ type: "vibrate", duration:
|
|
1044
|
-
{ type: "pause", duration:
|
|
1045
|
-
{ type: "vibrate", duration:
|
|
1046
|
-
{ type: "pause", duration:
|
|
1047
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
1268
|
+
{ type: "vibrate", duration: 35, intensity: 0.7 },
|
|
1059
1269
|
{ type: "pause", duration: 100, intensity: 0 },
|
|
1060
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
1305
|
+
{ type: "vibrate", duration: 25, intensity: 0.6 },
|
|
1096
1306
|
{ type: "pause", duration: 40, intensity: 0 },
|
|
1097
|
-
{ type: "vibrate", duration:
|
|
1307
|
+
{ type: "vibrate", duration: 25, intensity: 0.6 },
|
|
1098
1308
|
{ type: "pause", duration: 40, intensity: 0 },
|
|
1099
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
1316
|
+
{ type: "vibrate", duration: 30, intensity: 0.5 },
|
|
1107
1317
|
{ type: "pause", duration: 60, intensity: 0 },
|
|
1108
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
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:
|
|
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:
|
|
1130
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
1347
|
+
{ type: "vibrate", duration: 30, intensity: 0.5 },
|
|
1138
1348
|
{ type: "pause", duration: 50, intensity: 0 },
|
|
1139
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
1356
|
+
{ type: "vibrate", duration: 30, intensity: 0.5 },
|
|
1147
1357
|
{ type: "pause", duration: 80, intensity: 0 },
|
|
1148
|
-
{ type: "vibrate", duration:
|
|
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:
|
|
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:
|
|
1370
|
+
{ type: "vibrate", duration: 25, intensity: 0.4 },
|
|
1161
1371
|
{ type: "pause", duration: 30, intensity: 0 },
|
|
1162
|
-
{ type: "vibrate", duration:
|
|
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
|