import {FEEDBACK} from './feedback-params.mjs'; export const HAPTIC_KEY='number-swipe:haptics:v1'; export class Haptics{ constructor(api,storage,{now=()=>Date.now(),later=setTimeout,cancel=clearTimeout,preview=false}={}){Object.assign(this,{api,storage,now,later,cancel,preview});this.enabled=true;this.last=-Infinity;this.generation=0;this.timers=new Set();this.graded=true;try{this.enabled=storage.getItem(HAPTIC_KEY)!=='off';}catch{}} setEnabled(value){this.enabled=value;try{this.storage.setItem(HAPTIC_KEY,value?'on':'off');}catch{}if(!value)this.stop();} stop(){this.generation++;for(const t of this.timers)this.cancel(t);this.timers.clear();} pulse(type,force=false){ if(!this.enabled||this.preview||typeof this.api?.vibrateShort!=='function'||!force&&this.now()-this.last{if(generation!==this.generation||request!==this.request||this.now()-sentAt>FEEDBACK.hapticGapMs||!this.enabled)return;this.graded=false;try{this.api.vibrateShort({fail(){}});}catch{}}; try{this.api.vibrateShort(this.graded?{type,fail:fallback}:{fail(){}});}catch{if(this.graded)fallback();} } play(kind){if(!['move','undo','checkpoint','complete'].includes(kind))return;this.pulse(kind==='move'||kind==='undo'?'light':'medium',kind!=='move'&&kind!=='undo'); if(kind==='complete'&&this.enabled&&this.graded&&!this.preview&&typeof this.api?.vibrateShort==='function'){const generation=this.generation;const timer=this.later(()=>{this.timers.delete(timer);if(generation===this.generation&&this.graded)this.pulse('light',true);},FEEDBACK.completeHapticDelayMs);this.timers.add(timer);} } }