js.foresight 0.0.5 → 0.0.8
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/ForesightManager/ForesightDebugger.d.ts +8 -24
- package/dist/ForesightManager/ForesightDebugger.js +118 -75
- package/dist/ForesightManager/ForesightManager.d.ts +6 -12
- package/dist/ForesightManager/ForesightManager.js +144 -107
- package/dist/index.js +2 -20
- package/dist/types/types.d.ts +83 -6
- package/dist/types/types.js +1 -2
- package/package.json +2 -4
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import type { ForesightManager } from "./ForesightManager";
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
callback: () => void;
|
|
5
|
-
expandedRect: Rect | null;
|
|
6
|
-
isHovering: boolean;
|
|
7
|
-
isTrajectoryHit: boolean;
|
|
8
|
-
trajectoryHitTime: number;
|
|
9
|
-
};
|
|
10
|
-
export declare class IntentDebugger {
|
|
2
|
+
import type { ElementData, ForesightElement, ForesightManagerProps, Point } from "../types/types";
|
|
3
|
+
export declare class ForesightDebugger {
|
|
11
4
|
private foresightManagerInstance;
|
|
12
5
|
private shadowHost;
|
|
13
6
|
private shadowRoot;
|
|
@@ -17,22 +10,13 @@ export declare class IntentDebugger {
|
|
|
17
10
|
private debugTrajectoryLine;
|
|
18
11
|
private debugControlsContainer;
|
|
19
12
|
private debugStyleElement;
|
|
20
|
-
constructor(
|
|
21
|
-
initialize(links: Map<
|
|
22
|
-
positionHistorySize: number;
|
|
23
|
-
trajectoryPredictionTime: number;
|
|
24
|
-
enableMouseTrajectory: boolean;
|
|
25
|
-
}, currentPoint: Point, predictedPoint: Point): void;
|
|
13
|
+
constructor(intentManager: ForesightManager);
|
|
14
|
+
initialize(links: Map<ForesightElement, ElementData>, currentSettings: ForesightManagerProps, currentPoint: Point, predictedPoint: Point): void;
|
|
26
15
|
cleanup(): void;
|
|
27
|
-
createOrUpdateLinkOverlay(element:
|
|
28
|
-
removeLinkOverlay(element:
|
|
29
|
-
updateAllLinkVisuals(links: Map<
|
|
16
|
+
createOrUpdateLinkOverlay(element: ForesightElement, elementData: ElementData): void;
|
|
17
|
+
removeLinkOverlay(element: ForesightElement): void;
|
|
18
|
+
updateAllLinkVisuals(links: Map<ForesightElement, ElementData>): void;
|
|
30
19
|
updateTrajectoryVisuals(currentPoint: Point, predictedPoint: Point, enableMouseTrajectory: boolean): void;
|
|
31
20
|
private createDebugControls;
|
|
32
|
-
updateControlsState(settings:
|
|
33
|
-
positionHistorySize: number;
|
|
34
|
-
trajectoryPredictionTime: number;
|
|
35
|
-
enableMouseTrajectory: boolean;
|
|
36
|
-
}): void;
|
|
21
|
+
updateControlsState(settings: ForesightManagerProps): void;
|
|
37
22
|
}
|
|
38
|
-
export {};
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
"use client";
|
|
3
|
-
|
|
4
|
-
exports.IntentDebugger = void 0;
|
|
5
|
-
class IntentDebugger {
|
|
2
|
+
export class ForesightDebugger {
|
|
6
3
|
foresightManagerInstance;
|
|
7
4
|
shadowHost = null;
|
|
8
5
|
shadowRoot = null;
|
|
@@ -12,76 +9,87 @@ class IntentDebugger {
|
|
|
12
9
|
debugTrajectoryLine = null;
|
|
13
10
|
debugControlsContainer = null;
|
|
14
11
|
debugStyleElement = null;
|
|
15
|
-
constructor(
|
|
16
|
-
this.foresightManagerInstance =
|
|
12
|
+
constructor(intentManager) {
|
|
13
|
+
this.foresightManagerInstance = intentManager;
|
|
17
14
|
}
|
|
18
15
|
initialize(links, currentSettings, currentPoint, predictedPoint) {
|
|
19
16
|
if (typeof window === "undefined")
|
|
20
17
|
return;
|
|
21
18
|
this.cleanup();
|
|
22
19
|
this.shadowHost = document.createElement("div");
|
|
23
|
-
this.shadowHost.id = "
|
|
24
|
-
// Host itself should not capture pointer events unless specifically designed to.
|
|
20
|
+
this.shadowHost.id = "jsforesight-debugger-shadow-host";
|
|
25
21
|
this.shadowHost.style.pointerEvents = "none";
|
|
26
22
|
document.body.appendChild(this.shadowHost);
|
|
27
23
|
this.shadowRoot = this.shadowHost.attachShadow({ mode: "open" });
|
|
28
24
|
this.debugStyleElement = document.createElement("style");
|
|
29
25
|
this.debugStyleElement.textContent = `
|
|
30
|
-
#
|
|
26
|
+
#jsforesight-debug-container {
|
|
31
27
|
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
|
32
28
|
pointer-events: none; z-index: 9999;
|
|
33
29
|
}
|
|
34
|
-
.
|
|
30
|
+
.jsforesight-link-overlay {
|
|
35
31
|
position: absolute; border: 2px solid blue;
|
|
36
32
|
background-color: rgba(0, 0, 255, 0.1); box-sizing: border-box;
|
|
37
33
|
transition: opacity 0.2s ease, border-color 0.2s ease, background-color 0.2s ease, box-shadow 0.2s ease;
|
|
38
34
|
}
|
|
39
|
-
.
|
|
35
|
+
.jsforesight-link-overlay.active {
|
|
40
36
|
border-color: red; background-color: rgba(255, 0, 0, 0.1);
|
|
41
37
|
}
|
|
42
|
-
.
|
|
38
|
+
.jsforesight-link-overlay.trajectory-hit {
|
|
43
39
|
border-color: lime; background-color: rgba(0, 255, 0, 0.3);
|
|
44
40
|
box-shadow: 0 0 10px rgba(0, 255, 0, 0.8);
|
|
45
41
|
}
|
|
46
|
-
.
|
|
42
|
+
.jsforesight-expanded-overlay {
|
|
47
43
|
position: absolute; border: 1px dashed rgba(0, 0, 255, 0.5);
|
|
48
44
|
background-color: rgba(0, 0, 255, 0.05); box-sizing: border-box;
|
|
49
45
|
}
|
|
50
|
-
.
|
|
46
|
+
.jsforesight-mouse-predicted {
|
|
51
47
|
position: absolute; width: 20px; height: 20px; border-radius: 50%;
|
|
52
48
|
border: 2px solid orange; background-color: rgba(255, 165, 0, 0.3);
|
|
53
49
|
transform: translate(-50%, -50%); z-index: 10000;
|
|
54
50
|
}
|
|
55
|
-
.
|
|
51
|
+
.jsforesight-trajectory-line {
|
|
56
52
|
position: absolute; height: 2px; background-color: rgba(255, 100, 0, 0.5);
|
|
57
53
|
transform-origin: left center; z-index: 9999;
|
|
58
54
|
}
|
|
59
|
-
#
|
|
55
|
+
#jsforesight-debug-controls {
|
|
60
56
|
position: fixed; bottom: 10px; right: 10px;
|
|
61
|
-
background-color: rgba(0, 0, 0, 0.
|
|
62
|
-
border-radius: 5px; font-family:
|
|
63
|
-
z-index: 10001; pointer-events: auto;
|
|
57
|
+
background-color: rgba(0, 0, 0, 0.75); color: white; padding: 12px;
|
|
58
|
+
border-radius: 5px; font-family: Arial, sans-serif; font-size: 13px;
|
|
59
|
+
z-index: 10001; pointer-events: auto; display: flex; flex-direction: column; gap: 8px;
|
|
60
|
+
min-width: 300px; /* Ensure enough space for title and icon */
|
|
64
61
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
62
|
+
.jsforesight-debugger-title-container {
|
|
63
|
+
display: flex; align-items: center; justify-content: center; gap: 8px; margin-bottom: 8px;
|
|
64
|
+
}
|
|
65
|
+
.jsforesight-debugger-title-container h3 { margin: 0; font-size: 15px; }
|
|
66
|
+
#jsforesight-debug-controls label { display: flex; align-items: center; gap: 5px; cursor: pointer; }
|
|
67
|
+
#jsforesight-debug-controls input[type="range"] { flex-grow: 1; margin: 0 5px; cursor: pointer;}
|
|
68
|
+
#jsforesight-debug-controls input[type="checkbox"] { margin-right: 5px; cursor: pointer; }
|
|
69
|
+
#jsforesight-debug-controls .control-row { display: flex; align-items: center; justify-content: space-between; }
|
|
70
|
+
#jsforesight-debug-controls .control-row label { flex-basis: auto; /* Adjust for better spacing */ }
|
|
71
|
+
#jsforesight-debug-controls .control-row span:not(.jsforesight-info-icon) { min-width: 30px; text-align: right; }
|
|
72
|
+
.jsforesight-info-icon {
|
|
73
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
74
|
+
width: 16px; height: 16px; border-radius: 50%;
|
|
75
|
+
background-color: #555; color: white;
|
|
76
|
+
font-size: 10px; font-style: italic; font-weight: bold;
|
|
77
|
+
font-family: 'Georgia', serif;
|
|
78
|
+
cursor: help; user-select: none;
|
|
79
|
+
flex-shrink: 0; /* Prevent icon from shrinking */
|
|
71
80
|
}
|
|
72
|
-
#intent-debug-controls button:hover { background: #555; }
|
|
73
81
|
`;
|
|
74
82
|
this.shadowRoot.appendChild(this.debugStyleElement);
|
|
75
83
|
this.debugContainer = document.createElement("div");
|
|
76
|
-
this.debugContainer.id = "
|
|
84
|
+
this.debugContainer.id = "jsforesight-debug-container";
|
|
77
85
|
this.shadowRoot.appendChild(this.debugContainer);
|
|
78
86
|
this.debugPredictedMouseIndicator = document.createElement("div");
|
|
79
|
-
this.debugPredictedMouseIndicator.className = "
|
|
87
|
+
this.debugPredictedMouseIndicator.className = "jsforesight-mouse-predicted";
|
|
80
88
|
this.debugContainer.appendChild(this.debugPredictedMouseIndicator);
|
|
81
89
|
this.debugTrajectoryLine = document.createElement("div");
|
|
82
|
-
this.debugTrajectoryLine.className = "
|
|
90
|
+
this.debugTrajectoryLine.className = "jsforesight-trajectory-line";
|
|
83
91
|
this.debugContainer.appendChild(this.debugTrajectoryLine);
|
|
84
|
-
this.createDebugControls(currentSettings);
|
|
92
|
+
this.createDebugControls(currentSettings);
|
|
85
93
|
links.forEach((data, element) => {
|
|
86
94
|
this.createOrUpdateLinkOverlay(element, data);
|
|
87
95
|
});
|
|
@@ -91,37 +99,35 @@ class IntentDebugger {
|
|
|
91
99
|
this.shadowHost?.remove();
|
|
92
100
|
this.shadowHost = null;
|
|
93
101
|
this.shadowRoot = null;
|
|
102
|
+
this.debugLinkOverlays.clear();
|
|
94
103
|
}
|
|
95
|
-
createOrUpdateLinkOverlay(element,
|
|
96
|
-
if (!this.debugContainer)
|
|
104
|
+
createOrUpdateLinkOverlay(element, elementData) {
|
|
105
|
+
if (!this.debugContainer || !this.shadowRoot)
|
|
97
106
|
return;
|
|
98
107
|
let overlays = this.debugLinkOverlays.get(element);
|
|
99
108
|
if (!overlays) {
|
|
100
109
|
const linkOverlay = document.createElement("div");
|
|
101
|
-
linkOverlay.className = "
|
|
110
|
+
linkOverlay.className = "jsforesight-link-overlay";
|
|
102
111
|
this.debugContainer.appendChild(linkOverlay);
|
|
103
112
|
const expandedOverlay = document.createElement("div");
|
|
104
|
-
expandedOverlay.className = "
|
|
113
|
+
expandedOverlay.className = "jsforesight-expanded-overlay";
|
|
105
114
|
this.debugContainer.appendChild(expandedOverlay);
|
|
106
115
|
overlays = { linkOverlay, expandedOverlay };
|
|
107
116
|
this.debugLinkOverlays.set(element, overlays);
|
|
108
117
|
}
|
|
109
118
|
const { linkOverlay, expandedOverlay } = overlays;
|
|
110
119
|
const rect = element.getBoundingClientRect();
|
|
111
|
-
// Corrected: Since debugContainer is position:fixed top:0 left:0,
|
|
112
|
-
// getBoundingClientRect() coords are already relative to it.
|
|
113
120
|
linkOverlay.style.left = `${rect.left}px`;
|
|
114
121
|
linkOverlay.style.top = `${rect.top}px`;
|
|
115
122
|
linkOverlay.style.width = `${rect.width}px`;
|
|
116
123
|
linkOverlay.style.height = `${rect.height}px`;
|
|
117
|
-
linkOverlay.classList.toggle("trajectory-hit",
|
|
118
|
-
linkOverlay.classList.toggle("active",
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
expandedOverlay.style.
|
|
122
|
-
expandedOverlay.style.
|
|
123
|
-
expandedOverlay.style.
|
|
124
|
-
expandedOverlay.style.height = `${linkData.expandedRect.bottom - linkData.expandedRect.top}px`;
|
|
124
|
+
linkOverlay.classList.toggle("trajectory-hit", elementData.isTrajectoryHit);
|
|
125
|
+
linkOverlay.classList.toggle("active", elementData.isHovering);
|
|
126
|
+
if (elementData.elementBounds.expandedRect) {
|
|
127
|
+
expandedOverlay.style.left = `${elementData.elementBounds.expandedRect.left}px`;
|
|
128
|
+
expandedOverlay.style.top = `${elementData.elementBounds.expandedRect.top}px`;
|
|
129
|
+
expandedOverlay.style.width = `${elementData.elementBounds.expandedRect.right - elementData.elementBounds.expandedRect.left}px`;
|
|
130
|
+
expandedOverlay.style.height = `${elementData.elementBounds.expandedRect.bottom - elementData.elementBounds.expandedRect.top}px`;
|
|
125
131
|
expandedOverlay.style.display = "block";
|
|
126
132
|
}
|
|
127
133
|
else {
|
|
@@ -179,65 +185,102 @@ class IntentDebugger {
|
|
|
179
185
|
if (!this.shadowRoot)
|
|
180
186
|
return;
|
|
181
187
|
this.debugControlsContainer = document.createElement("div");
|
|
182
|
-
this.debugControlsContainer.id = "
|
|
188
|
+
this.debugControlsContainer.id = "jsforesight-debug-controls";
|
|
183
189
|
this.shadowRoot.appendChild(this.debugControlsContainer);
|
|
184
190
|
this.debugControlsContainer.innerHTML = `
|
|
185
|
-
<
|
|
186
|
-
|
|
187
|
-
<
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
191
|
+
<div class="jsforesight-debugger-title-container">
|
|
192
|
+
<h3>Foresight Debugger</h3>
|
|
193
|
+
<span class="jsforesight-info-icon" title="Changes made here are for the current session only and won't persist. Update initial values in the ForesightManager.initialize() props for permanent changes.">i</span>
|
|
194
|
+
</div>
|
|
195
|
+
<div class="control-row">
|
|
196
|
+
<label for="jsforesight-trajectory-enabled">
|
|
197
|
+
Enable Trajectory
|
|
198
|
+
<span class="jsforesight-info-icon" title="Toggles mouse movement prediction. If disabled, only direct hovers trigger actions.">i</span>
|
|
199
|
+
</label>
|
|
200
|
+
<input type="checkbox" id="jsforesight-trajectory-enabled" ${initialSettings.enableMouseTrajectory ? "checked" : ""}>
|
|
201
|
+
</div>
|
|
202
|
+
<div class="control-row">
|
|
203
|
+
<label for="jsforesight-history-size">
|
|
204
|
+
History Size
|
|
205
|
+
<span class="jsforesight-info-icon" title="Number of past mouse positions for velocity calculation (Min: 2, Max: 20). Higher values smooth predictions but add latency.">i</span>
|
|
206
|
+
</label>
|
|
207
|
+
<input type="range" id="jsforesight-history-size" min="2" max="20" value="${initialSettings.positionHistorySize}">
|
|
208
|
+
<span id="jsforesight-history-value">${initialSettings.positionHistorySize}</span>
|
|
209
|
+
</div>
|
|
210
|
+
<div class="control-row">
|
|
211
|
+
<label for="jsforesight-prediction-time">
|
|
212
|
+
Prediction Time (ms)
|
|
213
|
+
<span class="jsforesight-info-icon" title="How far (ms) to project trajectory (Min: 10, Max: 500). Larger values detect intent sooner.">i</span>
|
|
214
|
+
</label>
|
|
215
|
+
<input type="range" id="jsforesight-prediction-time" min="10" max="500" step="10" value="${initialSettings.trajectoryPredictionTime}">
|
|
216
|
+
<span id="jsforesight-prediction-value">${initialSettings.trajectoryPredictionTime}</span>
|
|
217
|
+
</div>
|
|
218
|
+
<div class="control-row">
|
|
219
|
+
<label for="jsforesight-throttle-delay">
|
|
220
|
+
Scroll/Resize Throttle (ms)
|
|
221
|
+
<span class="jsforesight-info-icon" title="Delay (ms) for recalculating element positions on resize/scroll (Min: 0, Max: 500). Higher values improve performance during rapid events.">i</span>
|
|
222
|
+
</label>
|
|
223
|
+
<input type="range" id="jsforesight-throttle-delay" min="0" max="500" step="10" value="${initialSettings.resizeScrollThrottleDelay}">
|
|
224
|
+
<span id="jsforesight-throttle-value">${initialSettings.resizeScrollThrottleDelay}</span>
|
|
225
|
+
</div>
|
|
199
226
|
`;
|
|
200
|
-
const enabledCheckbox = this.debugControlsContainer.querySelector("#
|
|
227
|
+
const enabledCheckbox = this.debugControlsContainer.querySelector("#jsforesight-trajectory-enabled");
|
|
201
228
|
enabledCheckbox.addEventListener("change", () => {
|
|
202
|
-
this.foresightManagerInstance.
|
|
203
|
-
|
|
229
|
+
this.foresightManagerInstance.alterGlobalSettings({
|
|
230
|
+
enableMouseTrajectory: enabledCheckbox.checked,
|
|
204
231
|
});
|
|
205
232
|
});
|
|
206
|
-
const historySlider = this.debugControlsContainer.querySelector("#
|
|
207
|
-
const historyValueSpan = this.debugControlsContainer.querySelector("#
|
|
233
|
+
const historySlider = this.debugControlsContainer.querySelector("#jsforesight-history-size");
|
|
234
|
+
const historyValueSpan = this.debugControlsContainer.querySelector("#jsforesight-history-value");
|
|
208
235
|
historySlider.addEventListener("input", () => {
|
|
209
236
|
const value = parseInt(historySlider.value);
|
|
210
237
|
historyValueSpan.textContent = value.toString();
|
|
211
|
-
this.foresightManagerInstance.
|
|
238
|
+
this.foresightManagerInstance.alterGlobalSettings({
|
|
239
|
+
positionHistorySize: value,
|
|
240
|
+
});
|
|
212
241
|
});
|
|
213
|
-
const predictionSlider = this.debugControlsContainer.querySelector("#
|
|
214
|
-
const predictionValueSpan = this.debugControlsContainer.querySelector("#
|
|
242
|
+
const predictionSlider = this.debugControlsContainer.querySelector("#jsforesight-prediction-time");
|
|
243
|
+
const predictionValueSpan = this.debugControlsContainer.querySelector("#jsforesight-prediction-value");
|
|
215
244
|
predictionSlider.addEventListener("input", () => {
|
|
216
245
|
const value = parseInt(predictionSlider.value);
|
|
217
246
|
predictionValueSpan.textContent = value.toString();
|
|
218
|
-
this.foresightManagerInstance.
|
|
219
|
-
|
|
247
|
+
this.foresightManagerInstance.alterGlobalSettings({
|
|
248
|
+
trajectoryPredictionTime: value,
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
const throttleSlider = this.debugControlsContainer.querySelector("#jsforesight-throttle-delay");
|
|
252
|
+
const throttleValueSpan = this.debugControlsContainer.querySelector("#jsforesight-throttle-value");
|
|
253
|
+
throttleSlider.addEventListener("input", () => {
|
|
254
|
+
const value = parseInt(throttleSlider.value);
|
|
255
|
+
throttleValueSpan.textContent = value.toString();
|
|
256
|
+
this.foresightManagerInstance.alterGlobalSettings({
|
|
257
|
+
resizeScrollThrottleDelay: value,
|
|
220
258
|
});
|
|
221
259
|
});
|
|
222
260
|
}
|
|
223
261
|
updateControlsState(settings) {
|
|
224
262
|
if (!this.debugControlsContainer)
|
|
225
263
|
return;
|
|
226
|
-
const enabledCheckbox = this.debugControlsContainer.querySelector("#
|
|
264
|
+
const enabledCheckbox = this.debugControlsContainer.querySelector("#jsforesight-trajectory-enabled");
|
|
227
265
|
if (enabledCheckbox)
|
|
228
266
|
enabledCheckbox.checked = settings.enableMouseTrajectory;
|
|
229
|
-
const historySlider = this.debugControlsContainer.querySelector("#
|
|
230
|
-
const historyValueSpan = this.debugControlsContainer.querySelector("#
|
|
267
|
+
const historySlider = this.debugControlsContainer.querySelector("#jsforesight-history-size");
|
|
268
|
+
const historyValueSpan = this.debugControlsContainer.querySelector("#jsforesight-history-value");
|
|
231
269
|
if (historySlider && historyValueSpan) {
|
|
232
270
|
historySlider.value = settings.positionHistorySize.toString();
|
|
233
271
|
historyValueSpan.textContent = settings.positionHistorySize.toString();
|
|
234
272
|
}
|
|
235
|
-
const predictionSlider = this.debugControlsContainer.querySelector("#
|
|
236
|
-
const predictionValueSpan = this.debugControlsContainer.querySelector("#
|
|
273
|
+
const predictionSlider = this.debugControlsContainer.querySelector("#jsforesight-prediction-time");
|
|
274
|
+
const predictionValueSpan = this.debugControlsContainer.querySelector("#jsforesight-prediction-value");
|
|
237
275
|
if (predictionSlider && predictionValueSpan) {
|
|
238
276
|
predictionSlider.value = settings.trajectoryPredictionTime.toString();
|
|
239
277
|
predictionValueSpan.textContent = settings.trajectoryPredictionTime.toString();
|
|
240
278
|
}
|
|
279
|
+
const throttleSlider = this.debugControlsContainer.querySelector("#jsforesight-throttle-delay");
|
|
280
|
+
const throttleValueSpan = this.debugControlsContainer.querySelector("#jsforesight-throttle-value");
|
|
281
|
+
if (throttleSlider && throttleValueSpan) {
|
|
282
|
+
throttleSlider.value = settings.resizeScrollThrottleDelay.toString();
|
|
283
|
+
throttleValueSpan.textContent = settings.resizeScrollThrottleDelay.toString();
|
|
284
|
+
}
|
|
241
285
|
}
|
|
242
286
|
}
|
|
243
|
-
exports.IntentDebugger = IntentDebugger;
|
|
@@ -1,30 +1,24 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ForesightCallback, ForesightManagerProps, ForesightElement, Rect } from "../types/types";
|
|
2
2
|
export declare class ForesightManager {
|
|
3
3
|
private static instance;
|
|
4
4
|
private links;
|
|
5
5
|
private isSetup;
|
|
6
6
|
private debugMode;
|
|
7
7
|
private debugger;
|
|
8
|
-
private
|
|
9
|
-
private trajectoryPredictionTime;
|
|
8
|
+
private globalSettings;
|
|
10
9
|
private positions;
|
|
11
|
-
private enableMouseTrajectory;
|
|
12
10
|
private currentPoint;
|
|
13
11
|
private predictedPoint;
|
|
14
12
|
private lastResizeScrollCallTimestamp;
|
|
15
13
|
private resizeScrollThrottleTimeoutId;
|
|
16
|
-
private readonly resizeScrollThrottleDelay;
|
|
17
14
|
private constructor();
|
|
18
|
-
static initialize(props?: Partial<
|
|
15
|
+
static initialize(props?: Partial<ForesightManagerProps>): ForesightManager;
|
|
19
16
|
static getInstance(): ForesightManager;
|
|
20
17
|
private checkTrajectoryHitExpiration;
|
|
21
|
-
|
|
18
|
+
private normalizeHitSlop;
|
|
19
|
+
register(element: ForesightElement, callback: ForesightCallback, hitSlop?: number | Rect): () => void;
|
|
22
20
|
private unregister;
|
|
23
|
-
|
|
24
|
-
historySize?: number;
|
|
25
|
-
predictionTime?: number;
|
|
26
|
-
enabled?: boolean;
|
|
27
|
-
}): void;
|
|
21
|
+
alterGlobalSettings(props?: Partial<ForesightManagerProps>): void;
|
|
28
22
|
private turnOnDebugMode;
|
|
29
23
|
private getExpandedRect;
|
|
30
24
|
private updateExpandedRect;
|
|
@@ -1,89 +1,108 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
"use client";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const ForesightDebugger_1 = require("./ForesightDebugger");
|
|
6
|
-
class ForesightManager {
|
|
2
|
+
import { ForesightDebugger } from "./ForesightDebugger";
|
|
3
|
+
export class ForesightManager {
|
|
7
4
|
static instance;
|
|
8
5
|
links = new Map();
|
|
9
6
|
isSetup = false;
|
|
10
|
-
debugMode = false;
|
|
7
|
+
debugMode = false; // Synced with globalSettings.debug
|
|
11
8
|
debugger = null;
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
globalSettings = {
|
|
10
|
+
debug: false,
|
|
11
|
+
enableMouseTrajectory: true,
|
|
12
|
+
positionHistorySize: 6,
|
|
13
|
+
trajectoryPredictionTime: 50,
|
|
14
|
+
defaultHitSlop: { top: 0, left: 0, right: 0, bottom: 0 },
|
|
15
|
+
resizeScrollThrottleDelay: 50,
|
|
16
|
+
};
|
|
14
17
|
positions = [];
|
|
15
|
-
enableMouseTrajectory = true;
|
|
16
18
|
currentPoint = { x: 0, y: 0 };
|
|
17
19
|
predictedPoint = { x: 0, y: 0 };
|
|
18
20
|
lastResizeScrollCallTimestamp = 0;
|
|
19
21
|
resizeScrollThrottleTimeoutId = null;
|
|
20
|
-
resizeScrollThrottleDelay = 50;
|
|
21
22
|
constructor() {
|
|
23
|
+
// Ensure defaultHitSlop is normalized if it's a number initially
|
|
24
|
+
this.globalSettings.defaultHitSlop = this.normalizeHitSlop(this.globalSettings.defaultHitSlop);
|
|
22
25
|
setInterval(this.checkTrajectoryHitExpiration.bind(this), 100);
|
|
23
26
|
}
|
|
24
27
|
static initialize(props) {
|
|
25
28
|
if (!ForesightManager.instance) {
|
|
26
29
|
ForesightManager.instance = new ForesightManager();
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
historySize: props.positionHistorySize,
|
|
31
|
-
predictionTime: props.trajectoryPredictionTime,
|
|
32
|
-
enabled: props.enableMouseTrajectory,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
if (props?.debug) {
|
|
36
|
-
this.instance.turnOnDebugMode();
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
if (this.instance.debugger) {
|
|
40
|
-
this.instance.debugMode = false;
|
|
41
|
-
this.instance.debugger.cleanup();
|
|
42
|
-
this.instance.debugger = null;
|
|
30
|
+
// Apply initial props, which also handles initial debugger setup
|
|
31
|
+
if (props) {
|
|
32
|
+
ForesightManager.instance.alterGlobalSettings(props);
|
|
43
33
|
}
|
|
34
|
+
else {
|
|
35
|
+
// If no props, but default globalSettings.debug is true, ensure debugger is on
|
|
36
|
+
if (ForesightManager.instance.globalSettings.debug) {
|
|
37
|
+
ForesightManager.instance.turnOnDebugMode();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else if (props) {
|
|
42
|
+
// Instance exists, apply new props (handles debugger lifecycle and UI updates)
|
|
43
|
+
ForesightManager.instance.alterGlobalSettings(props);
|
|
44
44
|
}
|
|
45
|
+
// Ensure internal debugMode flag is synced
|
|
46
|
+
ForesightManager.instance.debugMode = ForesightManager.instance.globalSettings.debug;
|
|
45
47
|
return ForesightManager.instance;
|
|
46
48
|
}
|
|
47
49
|
static getInstance() {
|
|
48
50
|
if (!ForesightManager.instance) {
|
|
49
|
-
return this.initialize();
|
|
51
|
+
return this.initialize(); // Initialize with defaults
|
|
50
52
|
}
|
|
51
53
|
return ForesightManager.instance;
|
|
52
54
|
}
|
|
53
55
|
checkTrajectoryHitExpiration() {
|
|
54
56
|
const now = performance.now();
|
|
55
57
|
let needsVisualUpdate = false;
|
|
56
|
-
const
|
|
57
|
-
this.links.forEach((
|
|
58
|
-
if (
|
|
58
|
+
const updatedForesightElements = [];
|
|
59
|
+
this.links.forEach((elementData, element) => {
|
|
60
|
+
if (elementData.isTrajectoryHit && now - elementData.trajectoryHitTime > 300) {
|
|
59
61
|
this.links.set(element, {
|
|
60
|
-
...
|
|
62
|
+
...elementData,
|
|
61
63
|
isTrajectoryHit: false,
|
|
62
64
|
});
|
|
63
65
|
needsVisualUpdate = true;
|
|
64
|
-
|
|
66
|
+
updatedForesightElements.push(element);
|
|
65
67
|
}
|
|
66
68
|
});
|
|
67
69
|
if (needsVisualUpdate && this.debugMode && this.debugger) {
|
|
68
|
-
|
|
70
|
+
updatedForesightElements.forEach((element) => {
|
|
69
71
|
const data = this.links.get(element);
|
|
70
72
|
if (data && this.debugger) {
|
|
71
|
-
console.log("asda");
|
|
72
73
|
this.debugger.createOrUpdateLinkOverlay(element, data);
|
|
73
74
|
}
|
|
74
75
|
});
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
normalizeHitSlop = (hitSlop) => {
|
|
79
|
+
if (typeof hitSlop === "number") {
|
|
80
|
+
return {
|
|
81
|
+
top: hitSlop,
|
|
82
|
+
left: hitSlop,
|
|
83
|
+
right: hitSlop,
|
|
84
|
+
bottom: hitSlop,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return hitSlop;
|
|
88
|
+
};
|
|
89
|
+
register(element, callback, hitSlop) {
|
|
90
|
+
const normalizedHitSlop = hitSlop
|
|
91
|
+
? this.normalizeHitSlop(hitSlop)
|
|
92
|
+
: this.globalSettings.defaultHitSlop; // Already normalized in constructor/alterGlobalSettings
|
|
93
|
+
const originalRect = element.getBoundingClientRect();
|
|
94
|
+
const newElementData = {
|
|
79
95
|
callback,
|
|
80
|
-
|
|
96
|
+
elementBounds: {
|
|
97
|
+
expandedRect: this.getExpandedRect(originalRect, normalizedHitSlop),
|
|
98
|
+
originalRect: originalRect,
|
|
99
|
+
hitSlop: normalizedHitSlop,
|
|
100
|
+
},
|
|
81
101
|
isHovering: false,
|
|
82
102
|
isTrajectoryHit: false,
|
|
83
103
|
trajectoryHitTime: 0,
|
|
84
104
|
};
|
|
85
|
-
this.links.set(element,
|
|
86
|
-
this.updateExpandedRect(element); // This will also update debug visuals if active
|
|
105
|
+
this.links.set(element, newElementData);
|
|
87
106
|
this.setupGlobalListeners();
|
|
88
107
|
if (this.debugMode && this.debugger) {
|
|
89
108
|
const data = this.links.get(element);
|
|
@@ -101,42 +120,68 @@ class ForesightManager {
|
|
|
101
120
|
this.removeGlobalListeners();
|
|
102
121
|
}
|
|
103
122
|
}
|
|
104
|
-
|
|
105
|
-
let
|
|
106
|
-
if (
|
|
107
|
-
this.positionHistorySize
|
|
108
|
-
|
|
123
|
+
alterGlobalSettings(props) {
|
|
124
|
+
let settingsActuallyChanged = false;
|
|
125
|
+
if (props?.positionHistorySize !== undefined &&
|
|
126
|
+
this.globalSettings.positionHistorySize !== props.positionHistorySize) {
|
|
127
|
+
this.globalSettings.positionHistorySize = props.positionHistorySize;
|
|
128
|
+
while (this.positions.length > this.globalSettings.positionHistorySize) {
|
|
109
129
|
this.positions.shift();
|
|
110
130
|
}
|
|
111
|
-
|
|
131
|
+
settingsActuallyChanged = true;
|
|
112
132
|
}
|
|
113
|
-
if (
|
|
114
|
-
this.trajectoryPredictionTime !==
|
|
115
|
-
this.trajectoryPredictionTime =
|
|
116
|
-
|
|
133
|
+
if (props?.trajectoryPredictionTime !== undefined &&
|
|
134
|
+
this.globalSettings.trajectoryPredictionTime !== props.trajectoryPredictionTime) {
|
|
135
|
+
this.globalSettings.trajectoryPredictionTime = props.trajectoryPredictionTime;
|
|
136
|
+
settingsActuallyChanged = true;
|
|
117
137
|
}
|
|
118
|
-
if (
|
|
119
|
-
this.enableMouseTrajectory
|
|
120
|
-
|
|
138
|
+
if (props?.enableMouseTrajectory !== undefined &&
|
|
139
|
+
this.globalSettings.enableMouseTrajectory !== props.enableMouseTrajectory) {
|
|
140
|
+
this.globalSettings.enableMouseTrajectory = props.enableMouseTrajectory;
|
|
141
|
+
settingsActuallyChanged = true;
|
|
121
142
|
}
|
|
122
|
-
if (
|
|
123
|
-
this.
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
143
|
+
if (props?.defaultHitSlop !== undefined) {
|
|
144
|
+
const newSlop = this.normalizeHitSlop(props.defaultHitSlop);
|
|
145
|
+
if (JSON.stringify(this.globalSettings.defaultHitSlop) !== JSON.stringify(newSlop)) {
|
|
146
|
+
this.globalSettings.defaultHitSlop = newSlop;
|
|
147
|
+
settingsActuallyChanged = true;
|
|
148
|
+
// Note: This won't update existing elements' hitSlop unless they are re-registered.
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (props?.resizeScrollThrottleDelay !== undefined &&
|
|
152
|
+
this.globalSettings.resizeScrollThrottleDelay !== props.resizeScrollThrottleDelay) {
|
|
153
|
+
this.globalSettings.resizeScrollThrottleDelay = props.resizeScrollThrottleDelay;
|
|
154
|
+
settingsActuallyChanged = true;
|
|
155
|
+
}
|
|
156
|
+
if (props?.debug !== undefined && this.globalSettings.debug !== props.debug) {
|
|
157
|
+
this.globalSettings.debug = props.debug;
|
|
158
|
+
if (this.globalSettings.debug) {
|
|
159
|
+
this.turnOnDebugMode();
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
if (this.debugger) {
|
|
163
|
+
this.debugger.cleanup();
|
|
164
|
+
this.debugger = null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
this.debugMode = this.globalSettings.debug;
|
|
168
|
+
}
|
|
169
|
+
if (settingsActuallyChanged && this.globalSettings.debug && this.debugger) {
|
|
170
|
+
this.debugger.updateControlsState(this.globalSettings);
|
|
171
|
+
this.debugger.updateTrajectoryVisuals(this.currentPoint, this.predictedPoint, this.globalSettings.enableMouseTrajectory);
|
|
129
172
|
}
|
|
130
173
|
}
|
|
131
174
|
turnOnDebugMode() {
|
|
132
|
-
this.debugMode = true;
|
|
175
|
+
this.debugMode = true; // Ensure this is true when method is called
|
|
133
176
|
if (!this.debugger) {
|
|
134
|
-
this.debugger = new
|
|
135
|
-
this.debugger.initialize(this.links,
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
177
|
+
this.debugger = new ForesightDebugger(this);
|
|
178
|
+
this.debugger.initialize(this.links, this.globalSettings, this.currentPoint, this.predictedPoint);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
// If debugger exists, ensure its controls are up-to-date with current globalSettings
|
|
182
|
+
// This could happen if debug was false, then true again, or settings changed.
|
|
183
|
+
this.debugger.updateControlsState(this.globalSettings);
|
|
184
|
+
this.debugger.updateTrajectoryVisuals(this.currentPoint, this.predictedPoint, this.globalSettings.enableMouseTrajectory);
|
|
140
185
|
}
|
|
141
186
|
}
|
|
142
187
|
getExpandedRect(baseRect, hitSlop) {
|
|
@@ -147,21 +192,18 @@ class ForesightManager {
|
|
|
147
192
|
bottom: baseRect.bottom + hitSlop.bottom,
|
|
148
193
|
};
|
|
149
194
|
}
|
|
150
|
-
updateExpandedRect(element) {
|
|
151
|
-
const
|
|
152
|
-
if (!
|
|
195
|
+
updateExpandedRect(element, hitSlop) {
|
|
196
|
+
const elementData = this.links.get(element);
|
|
197
|
+
if (!elementData)
|
|
153
198
|
return;
|
|
154
|
-
const expandedRect = this.getExpandedRect(element.getBoundingClientRect(),
|
|
155
|
-
|
|
156
|
-
left: 100,
|
|
157
|
-
right: 100,
|
|
158
|
-
bottom: 100,
|
|
159
|
-
});
|
|
160
|
-
if (expandedRect != linkData.expandedRect) {
|
|
161
|
-
console.log("updating rect");
|
|
199
|
+
const expandedRect = this.getExpandedRect(element.getBoundingClientRect(), hitSlop);
|
|
200
|
+
if (JSON.stringify(expandedRect) !== JSON.stringify(elementData.elementBounds.expandedRect)) {
|
|
162
201
|
this.links.set(element, {
|
|
163
|
-
...
|
|
164
|
-
|
|
202
|
+
...elementData,
|
|
203
|
+
elementBounds: {
|
|
204
|
+
...elementData.elementBounds,
|
|
205
|
+
expandedRect,
|
|
206
|
+
},
|
|
165
207
|
});
|
|
166
208
|
}
|
|
167
209
|
if (this.debugMode && this.debugger) {
|
|
@@ -171,8 +213,8 @@ class ForesightManager {
|
|
|
171
213
|
}
|
|
172
214
|
}
|
|
173
215
|
updateAllRects() {
|
|
174
|
-
this.links.forEach((
|
|
175
|
-
this.updateExpandedRect(element);
|
|
216
|
+
this.links.forEach((data, element) => {
|
|
217
|
+
this.updateExpandedRect(element, data.elementBounds.hitSlop);
|
|
176
218
|
});
|
|
177
219
|
}
|
|
178
220
|
predictMousePosition = (point) => {
|
|
@@ -180,7 +222,7 @@ class ForesightManager {
|
|
|
180
222
|
const currentPosition = { point, time: now };
|
|
181
223
|
const { x, y } = point;
|
|
182
224
|
this.positions.push(currentPosition);
|
|
183
|
-
if (this.positions.length > this.positionHistorySize) {
|
|
225
|
+
if (this.positions.length > this.globalSettings.positionHistorySize) {
|
|
184
226
|
this.positions.shift();
|
|
185
227
|
}
|
|
186
228
|
if (this.positions.length < 2) {
|
|
@@ -196,7 +238,7 @@ class ForesightManager {
|
|
|
196
238
|
const dy = last.point.y - first.point.y;
|
|
197
239
|
const vx = dx / dt;
|
|
198
240
|
const vy = dy / dt;
|
|
199
|
-
const trajectoryPredictionTimeInSeconds = this.trajectoryPredictionTime / 1000;
|
|
241
|
+
const trajectoryPredictionTimeInSeconds = this.globalSettings.trajectoryPredictionTime / 1000;
|
|
200
242
|
const predictedX = x + vx * trajectoryPredictionTimeInSeconds;
|
|
201
243
|
const predictedY = y + vy * trajectoryPredictionTimeInSeconds;
|
|
202
244
|
return { x: predictedX, y: predictedY };
|
|
@@ -216,22 +258,21 @@ class ForesightManager {
|
|
|
216
258
|
};
|
|
217
259
|
handleMouseMove = (e) => {
|
|
218
260
|
this.currentPoint = { x: e.clientX, y: e.clientY };
|
|
219
|
-
this.predictedPoint = this.enableMouseTrajectory
|
|
261
|
+
this.predictedPoint = this.globalSettings.enableMouseTrajectory
|
|
220
262
|
? this.predictMousePosition(this.currentPoint)
|
|
221
263
|
: this.currentPoint;
|
|
222
264
|
const linksToUpdateInDebugger = [];
|
|
223
|
-
this.links.forEach((
|
|
224
|
-
if (!
|
|
265
|
+
this.links.forEach((elementData, element) => {
|
|
266
|
+
if (!elementData.elementBounds.expandedRect)
|
|
225
267
|
return;
|
|
226
|
-
const { isHoveringInArea, shouldRunCallback } = this.isMouseInExpandedArea(
|
|
268
|
+
const { isHoveringInArea, shouldRunCallback } = this.isMouseInExpandedArea(elementData.elementBounds.expandedRect, this.currentPoint, elementData.isHovering);
|
|
227
269
|
let linkStateChanged = false;
|
|
228
|
-
if (this.enableMouseTrajectory && !isHoveringInArea) {
|
|
229
|
-
if (this.pointIntersectsRect(this.predictedPoint.x, this.predictedPoint.y,
|
|
230
|
-
if (!
|
|
231
|
-
|
|
232
|
-
console.log("trajectory");
|
|
270
|
+
if (this.globalSettings.enableMouseTrajectory && !isHoveringInArea) {
|
|
271
|
+
if (this.pointIntersectsRect(this.predictedPoint.x, this.predictedPoint.y, elementData.elementBounds.expandedRect)) {
|
|
272
|
+
if (!elementData.isTrajectoryHit) {
|
|
273
|
+
elementData.callback();
|
|
233
274
|
this.links.set(element, {
|
|
234
|
-
...
|
|
275
|
+
...elementData,
|
|
235
276
|
isTrajectoryHit: true,
|
|
236
277
|
trajectoryHitTime: performance.now(),
|
|
237
278
|
isHovering: isHoveringInArea,
|
|
@@ -240,10 +281,11 @@ class ForesightManager {
|
|
|
240
281
|
}
|
|
241
282
|
}
|
|
242
283
|
}
|
|
243
|
-
if (
|
|
284
|
+
if (elementData.isHovering !== isHoveringInArea) {
|
|
244
285
|
this.links.set(element, {
|
|
245
|
-
...
|
|
286
|
+
...elementData,
|
|
246
287
|
isHovering: isHoveringInArea,
|
|
288
|
+
// Preserve trajectory hit state if it was already hit
|
|
247
289
|
isTrajectoryHit: this.links.get(element).isTrajectoryHit,
|
|
248
290
|
trajectoryHitTime: this.links.get(element).trajectoryHitTime,
|
|
249
291
|
});
|
|
@@ -253,10 +295,9 @@ class ForesightManager {
|
|
|
253
295
|
linksToUpdateInDebugger.push(element);
|
|
254
296
|
}
|
|
255
297
|
if (shouldRunCallback) {
|
|
256
|
-
if (!
|
|
257
|
-
(
|
|
258
|
-
|
|
259
|
-
console.log("hover");
|
|
298
|
+
if (!elementData.isTrajectoryHit ||
|
|
299
|
+
(elementData.isTrajectoryHit && !this.globalSettings.enableMouseTrajectory)) {
|
|
300
|
+
elementData.callback();
|
|
260
301
|
}
|
|
261
302
|
}
|
|
262
303
|
});
|
|
@@ -266,17 +307,17 @@ class ForesightManager {
|
|
|
266
307
|
if (data)
|
|
267
308
|
this.debugger.createOrUpdateLinkOverlay(element, data);
|
|
268
309
|
});
|
|
269
|
-
this.debugger.updateTrajectoryVisuals(this.currentPoint, this.predictedPoint, this.enableMouseTrajectory);
|
|
310
|
+
this.debugger.updateTrajectoryVisuals(this.currentPoint, this.predictedPoint, this.globalSettings.enableMouseTrajectory);
|
|
270
311
|
}
|
|
271
312
|
};
|
|
272
|
-
// Throttled handler for resize and scroll events
|
|
273
313
|
handleResizeOrScroll = () => {
|
|
274
314
|
if (this.resizeScrollThrottleTimeoutId) {
|
|
275
315
|
clearTimeout(this.resizeScrollThrottleTimeoutId);
|
|
276
316
|
}
|
|
277
317
|
const now = performance.now();
|
|
278
318
|
const timeSinceLastCall = now - this.lastResizeScrollCallTimestamp;
|
|
279
|
-
|
|
319
|
+
const currentDelay = this.globalSettings.resizeScrollThrottleDelay;
|
|
320
|
+
if (timeSinceLastCall >= currentDelay) {
|
|
280
321
|
this.updateAllRects();
|
|
281
322
|
this.lastResizeScrollCallTimestamp = now;
|
|
282
323
|
this.resizeScrollThrottleTimeoutId = null;
|
|
@@ -286,24 +327,21 @@ class ForesightManager {
|
|
|
286
327
|
this.updateAllRects();
|
|
287
328
|
this.lastResizeScrollCallTimestamp = performance.now();
|
|
288
329
|
this.resizeScrollThrottleTimeoutId = null;
|
|
289
|
-
},
|
|
330
|
+
}, currentDelay - timeSinceLastCall);
|
|
290
331
|
}
|
|
291
332
|
};
|
|
292
333
|
setupGlobalListeners() {
|
|
293
334
|
if (this.isSetup)
|
|
294
335
|
return;
|
|
295
336
|
document.addEventListener("mousemove", this.handleMouseMove);
|
|
296
|
-
// Use the throttled handler for resize and scroll
|
|
297
337
|
window.addEventListener("resize", this.handleResizeOrScroll);
|
|
298
338
|
window.addEventListener("scroll", this.handleResizeOrScroll);
|
|
299
339
|
this.isSetup = true;
|
|
300
340
|
}
|
|
301
341
|
removeGlobalListeners() {
|
|
302
342
|
document.removeEventListener("mousemove", this.handleMouseMove);
|
|
303
|
-
// Remove the throttled handler for resize and scroll
|
|
304
343
|
window.removeEventListener("resize", this.handleResizeOrScroll);
|
|
305
344
|
window.removeEventListener("scroll", this.handleResizeOrScroll);
|
|
306
|
-
// Clear any pending timeout for the throttled handler
|
|
307
345
|
if (this.resizeScrollThrottleTimeoutId) {
|
|
308
346
|
clearTimeout(this.resizeScrollThrottleTimeoutId);
|
|
309
347
|
this.resizeScrollThrottleTimeoutId = null;
|
|
@@ -311,4 +349,3 @@ class ForesightManager {
|
|
|
311
349
|
this.isSetup = false;
|
|
312
350
|
}
|
|
313
351
|
}
|
|
314
|
-
exports.ForesightManager = ForesightManager;
|
package/dist/index.js
CHANGED
|
@@ -1,22 +1,4 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.ForesightManager = void 0;
|
|
18
1
|
// Export the ForesightManager (currently called IntentManager)
|
|
19
|
-
|
|
20
|
-
Object.defineProperty(exports, "ForesightManager", { enumerable: true, get: function () { return ForesightManager_1.ForesightManager; } });
|
|
2
|
+
export { ForesightManager } from "./ForesightManager/ForesightManager";
|
|
21
3
|
// Export any types you need
|
|
22
|
-
|
|
4
|
+
export * from "./types/types";
|
package/dist/types/types.d.ts
CHANGED
|
@@ -4,26 +4,103 @@ export type Rect = {
|
|
|
4
4
|
right: number;
|
|
5
5
|
bottom: number;
|
|
6
6
|
};
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* A callback function that is executed when a foresight interaction
|
|
9
|
+
* (e.g., hover, trajectory hit) occurs on a registered element.
|
|
10
|
+
* Only triggers ones per interaction
|
|
11
|
+
*/
|
|
12
|
+
export type ForesightCallback = () => void;
|
|
13
|
+
/**
|
|
14
|
+
* Represents the HTML element that is being tracked by the ForesightManager.
|
|
15
|
+
* This is typically a standard DOM `Element`.
|
|
16
|
+
*/
|
|
17
|
+
export type ForesightElement = Element;
|
|
18
|
+
/**
|
|
19
|
+
* Represents a mouse position captured at a specific point in time.
|
|
20
|
+
* Used for tracking mouse movement history.
|
|
21
|
+
*/
|
|
9
22
|
export type MousePosition = {
|
|
23
|
+
/** The (x, y) coordinates of the mouse. */
|
|
10
24
|
point: Point;
|
|
25
|
+
/** The timestamp (e.g., from `performance.now()`) when this position was recorded. */
|
|
11
26
|
time: number;
|
|
12
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* Represents a 2D point with x and y coordinates.
|
|
30
|
+
*/
|
|
13
31
|
export type Point = {
|
|
14
32
|
x: number;
|
|
15
33
|
y: number;
|
|
16
34
|
};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Internal type representing the calculated boundaries of a foresight element,
|
|
37
|
+
* including its original dimensions and the expanded hit area.
|
|
38
|
+
* (This type is not exported, but commenting it is good practice for maintainability)
|
|
39
|
+
*/
|
|
40
|
+
type ElementBounds = {
|
|
41
|
+
/** The expanded rectangle, including hitSlop, used for interaction detection. */
|
|
42
|
+
expandedRect: Rect;
|
|
43
|
+
/** The original bounding rectangle of the element, as returned by `getBoundingClientRect()`. */
|
|
44
|
+
originalRect: DOMRect;
|
|
45
|
+
/** The hit slop values applied to this element. */
|
|
46
|
+
hitSlop: Rect;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Represents the data associated with a registered foresight element.
|
|
50
|
+
* This includes its callback, boundary information, and current interaction state.
|
|
51
|
+
*/
|
|
52
|
+
export type ElementData = {
|
|
53
|
+
/** The callback function to execute on interaction. */
|
|
54
|
+
callback: ForesightCallback;
|
|
55
|
+
/** The boundary information for the element. */
|
|
56
|
+
elementBounds: ElementBounds;
|
|
57
|
+
/** True if the mouse cursor is currently hovering over the element's expanded bounds. */
|
|
20
58
|
isHovering: boolean;
|
|
59
|
+
/** True if the predicted mouse trajectory has intersected the element's expanded bounds. */
|
|
21
60
|
isTrajectoryHit: boolean;
|
|
61
|
+
/** The timestamp when the last trajectory hit occurred. */
|
|
22
62
|
trajectoryHitTime: number;
|
|
23
63
|
};
|
|
24
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Configuration options for the ForesightManager
|
|
66
|
+
*/
|
|
67
|
+
export type ForesightManagerProps = {
|
|
68
|
+
/**
|
|
69
|
+
* Number of mouse positions to keep in history for trajectory calculation.
|
|
70
|
+
* A higher number might lead to smoother but slightly delayed predictions.
|
|
71
|
+
* @default 6
|
|
72
|
+
*/
|
|
25
73
|
positionHistorySize: number;
|
|
74
|
+
/**
|
|
75
|
+
* How far ahead (in milliseconds) to predict the mouse trajectory.
|
|
76
|
+
* A larger value means the prediction extends further into the future. (meaning it will trigger callbacks sooner)
|
|
77
|
+
* @default 50
|
|
78
|
+
*/
|
|
26
79
|
trajectoryPredictionTime: number;
|
|
80
|
+
/**
|
|
81
|
+
* Whether to enable mouse trajectory prediction.
|
|
82
|
+
* If false, only direct hover/interaction is considered.
|
|
83
|
+
* @default true
|
|
84
|
+
*/
|
|
27
85
|
enableMouseTrajectory: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Whether to show visual debugging information on the screen.
|
|
88
|
+
* This includes overlays for elements, hit slop areas, and the predicted mouse path.
|
|
89
|
+
* @default false
|
|
90
|
+
*/
|
|
28
91
|
debug: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Default hit slop to apply to all registered elements if no specific
|
|
94
|
+
* hit slop is provided during registration.
|
|
95
|
+
* Can be a single number for uniform slop on all sides, or a Rect object
|
|
96
|
+
* for different values per side.
|
|
97
|
+
* @default { top: 0, left: 0, right: 0, bottom: 0 }
|
|
98
|
+
*/
|
|
99
|
+
defaultHitSlop: Rect | number;
|
|
100
|
+
/**
|
|
101
|
+
* Amount of time in ms the element bounds get recalculated on scroll/resize of the page.
|
|
102
|
+
* @default 50
|
|
103
|
+
*/
|
|
104
|
+
resizeScrollThrottleDelay: number;
|
|
29
105
|
};
|
|
106
|
+
export {};
|
package/dist/types/types.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "js.foresight",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"
|
|
9
|
-
"build": "pnpm clean && tsc",
|
|
10
|
-
"prepublishOnly": "pnpm build",
|
|
8
|
+
"build": "tsc --build --clean && tsc",
|
|
11
9
|
"test": "cd ./test_project && pnpm dev"
|
|
12
10
|
},
|
|
13
11
|
"exports": {
|