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