UNPKG

js.foresight

Version:

Predicts where users will click based on mouse movement, keyboard navigation, and scroll behavior. Includes touch device support. Triggers callbacks before interactions happen to enable prefetching and faster UI responses. Works with any framework.

2 lines (1 loc) 6.41 kB
import{c as f,d as b,e as d}from"./chunk-PAYO6NXN.js";import{a as m}from"./chunk-44N4MCQB.js";import{a as l}from"./chunk-AODZNE3S.js";function y(n,i,t,e){let r=performance.now();i.add({point:{x:n.x,y:n.y},time:r});let{x:o,y:s}=n;if(i.length<2){e.x=o,e.y=s;return}let[c,a]=i.getFirstLast();if(!c||!a){e.x=o,e.y=s;return}let p=(a.time-c.time)*.001;if(p===0){e.x=o,e.y=s;return}let v=a.point.x-c.point.x,T=a.point.y-c.point.y,x=v/p,M=T/p,P=t*.001;e.x=o+x*P,e.y=s+M*P}var h=class extends l{constructor(t){super(t.dependencies);this.moduleName="MousePredictor";this.trajectoryPositions=t.trajectoryPositions}updatePointerState(t){let e=this.trajectoryPositions.currentPoint;e.x=t.clientX,e.y=t.clientY,this.settings.enableMousePrediction?y(e,this.trajectoryPositions.positions,this.settings.trajectoryPredictionTime,this.trajectoryPositions.predictedPoint):(this.trajectoryPositions.predictedPoint.x=e.x,this.trajectoryPositions.predictedPoint.y=e.y)}processMouseMovement(t){this.updatePointerState(t);let e=this.settings.enableMousePrediction,r=this.trajectoryPositions.currentPoint;for(let o of this.elements.values()){if(!o.isIntersectingWithViewport||!o.callbackInfo.isCallbackActive||o.callbackInfo.isRunningCallback)continue;let s=o.elementBounds.expandedRect;if(e)m(r,this.trajectoryPositions.predictedPoint,s)&&this.callCallback(o,{kind:"mouse",subType:"trajectory"});else if(d(r,s)){this.callCallback(o,{kind:"mouse",subType:"hover"});return}}this.hasListeners("mouseTrajectoryUpdate")&&this.emit({type:"mouseTrajectoryUpdate",predictionEnabled:e,trajectoryPositions:this.trajectoryPositions})}onDisconnect(){}onConnect(){}};import{PositionObserver as j}from"position-observer";var u=class{constructor(i){this.head=0;this.count=0;if(i<=0)throw new Error("CircularBuffer capacity must be greater than 0");this.capacity=i,this.buffer=new Array(i)}add(i){this.buffer[this.head]=i,this.head=(this.head+1)%this.capacity,this.count<this.capacity&&this.count++}getFirst(){if(this.count!==0)return this.count<this.capacity?this.buffer[0]:this.buffer[this.head]}getLast(){if(this.count!==0){if(this.count<this.capacity)return this.buffer[this.count-1];{let i=(this.head-1+this.capacity)%this.capacity;return this.buffer[i]}}}getFirstLast(){if(this.count===0)return[void 0,void 0];if(this.count===1){let e=this.count<this.capacity?this.buffer[0]:this.buffer[this.head];return[e,e]}let i=this.getFirst(),t=this.getLast();return[i,t]}resize(i){if(i<=0)throw new Error("CircularBuffer capacity must be greater than 0");if(i===this.capacity)return;let t=this.getAllItems();if(this.capacity=i,this.buffer=new Array(i),this.head=0,this.count=0,t.length>i){let e=t.slice(-i);for(let r of e)this.add(r)}else for(let e of t)this.add(e)}getAllItems(){if(this.count===0)return[];let i=new Array(this.count);if(this.count<this.capacity)for(let t=0;t<this.count;t++)i[t]=this.buffer[t];else{let t=this.head;for(let e=0;e<this.capacity;e++){let r=(t+e)%this.capacity;i[e]=this.buffer[r]}}return i}clear(){this.head=0,this.count=0}get length(){return this.count}get size(){return this.capacity}get isFull(){return this.count===this.capacity}get isEmpty(){return this.count===0}};var g=class extends l{constructor(t){super(t);this.moduleName="DesktopHandler";this.tabPredictor=null;this.scrollPredictor=null;this.positionObserver=null;this.trajectoryPositions={positions:new u(8),currentPoint:{x:0,y:0},predictedPoint:{x:0,y:0}};this.handlePositionChange=t=>{let e=this.settings.enableScrollPrediction;for(let r of t){let o=this.elements.get(r.target);o&&(e?this.scrollPredictor?.handleScrollPrefetch(o,r.boundingClientRect):this.checkForMouseHover(o),this.handlePositionChangeDataUpdates(o,r))}e&&this.scrollPredictor?.resetScrollProps()};this.checkForMouseHover=t=>{d(this.trajectoryPositions.currentPoint,t.elementBounds.expandedRect)&&this.callCallback(t,{kind:"mouse",subType:"hover"})};this.handlePositionChangeDataUpdates=(t,e)=>{let r=[],o=e.isIntersecting;t.isIntersectingWithViewport!==o&&(r.push("visibility"),t.isIntersectingWithViewport=o),o&&!b(e.boundingClientRect,t.elementBounds.originalRect)&&(r.push("bounds"),t.elementBounds={hitSlop:t.elementBounds.hitSlop,originalRect:e.boundingClientRect,expandedRect:f(e.boundingClientRect,t.elementBounds.hitSlop)}),r.length&&this.hasListeners("elementDataUpdated")&&this.emit({type:"elementDataUpdated",elementData:t,updatedProps:r})};this.processMouseMovement=t=>this.mousePredictor.processMouseMovement(t);this.invalidateTabCache=()=>this.tabPredictor?.invalidateCache();this.observeElement=t=>this.positionObserver?.observe(t);this.unobserveElement=t=>this.positionObserver?.unobserve(t);this.connectTabPredictor=async()=>{if(!this.tabPredictor){let{TabPredictor:t}=await import("./TabPredictor-HA2SV3CY.js");this.tabPredictor=new t(this.storedDependencies),this.devLog("TabPredictor lazy loaded")}this.tabPredictor.connect()};this.connectScrollPredictor=async()=>{if(!this.scrollPredictor){let{ScrollPredictor:t}=await import("./ScrollPredictor-Y7NELMBI.js");this.scrollPredictor=new t({dependencies:this.storedDependencies,trajectoryPositions:this.trajectoryPositions}),this.devLog("ScrollPredictor lazy loaded")}this.scrollPredictor.connect()};this.connectMousePredictor=()=>this.mousePredictor.connect();this.disconnectTabPredictor=()=>this.tabPredictor?.disconnect();this.disconnectScrollPredictor=()=>this.scrollPredictor?.disconnect();this.disconnectMousePredictor=()=>this.mousePredictor.disconnect();this.storedDependencies=t,this.mousePredictor=new h({dependencies:t,trajectoryPositions:this.trajectoryPositions})}onConnect(){this.settings.enableTabPrediction&&this.connectTabPredictor(),this.settings.enableScrollPrediction&&this.connectScrollPredictor(),this.connectMousePredictor(),this.positionObserver=new j(this.handlePositionChange);let t=["mouse"];this.settings.enableTabPrediction&&t.push("tab (loading...)"),this.settings.enableScrollPrediction&&t.push("scroll (loading...)"),this.devLog(`Connected predictors: [${t.join(", ")}] and PositionObserver`);for(let e of this.elements.keys())this.positionObserver.observe(e)}onDisconnect(){this.disconnectMousePredictor(),this.disconnectTabPredictor(),this.disconnectScrollPredictor(),this.positionObserver?.disconnect(),this.positionObserver=null}get loadedPredictors(){return{mouse:this.mousePredictor!==null,tab:this.tabPredictor!==null,scroll:this.scrollPredictor!==null}}};export{g as DesktopHandler};