@supermousejs/core 2.5.0-beta.1 → 2.5.0-beta.3

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/index.mjs CHANGED
@@ -1,62 +1,76 @@
1
- const w = {
2
- rules: [
3
- { selector: "input", native: !0, hide: !0 },
4
- { selector: "textarea", native: !0, hide: !0 },
5
- { selector: "select", native: !0, hide: !0 },
6
- { selector: "[contenteditable]", native: !0, hide: !0 },
7
- { selector: "a", native: !1, hide: !0 },
8
- { selector: "button", native: !1, hide: !0 },
9
- { selector: '[role="button"]', native: !1, hide: !0 },
10
- { selector: "[tabindex]", native: !1, hide: !0 }
1
+ const S = {
2
+ native: ["input", "textarea", "select", "[contenteditable]"],
3
+ hide: [
4
+ "input",
5
+ "textarea",
6
+ "select",
7
+ "[contenteditable]",
8
+ "a",
9
+ "button",
10
+ '[role="button"]',
11
+ "[tabindex]",
12
+ "label"
11
13
  ]
12
14
  };
13
- function x(i) {
14
- if (!i) return w;
15
- if ("rules" in i) return i;
16
- const t = [], e = new Set(i.native ?? []), s = new Set(i.hide ?? []), n = /* @__PURE__ */ new Set([...e, ...s]);
17
- for (const r of n)
18
- t.push({
19
- selector: r,
20
- native: e.has(r),
21
- hide: s.has(r)
22
- });
23
- return { rules: t };
24
- }
25
- function T(i) {
26
- const t = [], e = [];
27
- for (const s of i.rules)
28
- s.native !== !1 && t.push(s.selector), s.hide !== !1 && e.push(s.selector);
29
- return { native: t, hide: e };
15
+ function b(n) {
16
+ return n ? {
17
+ native: n.native ?? [],
18
+ hide: n.hide ?? []
19
+ } : S;
30
20
  }
31
- let L = 0;
32
- class A {
21
+ let y = 0;
22
+ class P {
23
+ element;
24
+ scopeClass;
25
+ hideClass;
26
+ container;
27
+ currentCursorState = null;
28
+ originalContainerPosition = "";
29
+ originalContainerCursor = "";
30
+ _visible = !0;
33
31
  constructor(t, e) {
34
- if (this.container = t, !t || !(t instanceof HTMLElement))
35
- throw new Error(`[Supermouse] Invalid container: ${t}. Must be an HTMLElement.`);
36
- t.isConnected;
37
- const s = L++;
38
- this.scopeClass = `supermouse-scope-${s}`, this.hideClass = `supermouse-hide-${s}`;
39
- const n = t === document.body;
40
- if (this.element = document.createElement("div"), Object.assign(this.element.style, {
41
- position: n ? "fixed" : "absolute",
32
+ if (!t || !(t instanceof HTMLElement))
33
+ throw new Error(
34
+ `[Supermouse] Invalid container: ${t}. Must be an HTMLElement.`
35
+ );
36
+ const s = t.hasAttribute("data-supermouse-placeholder");
37
+ !t.isConnected && !s && console.warn(
38
+ "[Supermouse] container is not attached to the document — stage sizing/positioning will be wrong until it is."
39
+ );
40
+ const i = y++;
41
+ this.scopeClass = `supermouse-scope-${i}`, this.hideClass = `supermouse-hide-${i}`, this.element = document.createElement("div"), Object.assign(this.element.style, {
42
+ position: "absolute",
42
43
  inset: "0px",
43
44
  pointerEvents: "none",
44
45
  zIndex: String(e),
45
46
  opacity: "1",
46
47
  transition: "opacity 0.15s ease"
47
- }), !n) {
48
- const r = window.getComputedStyle(t);
49
- this.originalContainerPosition = r.position, r.position === "static" && (t.style.position = "relative");
48
+ }), this.container = t, this._attach(t);
49
+ }
50
+ _attach(t) {
51
+ const e = t === document.body;
52
+ if (this.element.style.position = e ? "fixed" : "absolute", e)
53
+ this.originalContainerPosition = "";
54
+ else {
55
+ const s = window.getComputedStyle(t);
56
+ this.originalContainerPosition = s.position, s.position === "static" && (t.style.position = "relative");
50
57
  }
51
58
  this.originalContainerCursor = t.style.cursor, t.appendChild(this.element), t.classList.add("supermouse-scope", this.scopeClass);
52
59
  }
53
- container;
54
- element;
55
- scopeClass;
56
- hideClass;
57
- currentCursorState = null;
58
- originalContainerPosition = "";
59
- originalContainerCursor = "";
60
+ _detach() {
61
+ const t = this.container;
62
+ t.classList.remove("supermouse-scope", this.scopeClass, this.hideClass), t !== document.body && this.originalContainerPosition === "static" && (t.style.position = ""), t.style.cursor = this.originalContainerCursor, this.element.remove();
63
+ }
64
+ /**
65
+ * Rebinds this stage to a new container. The previous container is
66
+ * restored to its pre-Supermouse state, and the current cursor
67
+ * suppression is re-applied to the new container.
68
+ */
69
+ attach(t) {
70
+ if (this.container === t) return;
71
+ const e = this.currentCursorState;
72
+ this._detach(), this.container = t, this._attach(t), this.currentCursorState = null, e && this.setNativeCursor(e);
73
+ }
60
74
  get containerElement() {
61
75
  return this.container;
62
76
  }
@@ -69,58 +83,96 @@ class A {
69
83
  return `:not(.${this.scopeClass} .supermouse-scope):not(.${this.scopeClass} .supermouse-scope *)`;
70
84
  }
71
85
  setVisibility(t) {
72
- this.element.style.opacity = t ? "1" : "0";
86
+ t !== this._visible && (this._visible = t, this.element.style.opacity = t ? "1" : "0");
73
87
  }
74
88
  setNativeCursor(t) {
75
- t !== this.currentCursorState && (this.currentCursorState = t, this.container.classList.toggle(this.hideClass, t === "none"), t === "none" ? this.container.style.cursor = "none" : this.container === document.body ? this.container.style.cursor = this.originalContainerCursor : this.container.style.cursor = this.originalContainerCursor || "auto");
89
+ t !== this.currentCursorState && (this.currentCursorState = t, this.container.classList.toggle(this.hideClass, t === "none"));
76
90
  }
77
91
  destroy() {
78
- this.element.remove(), this.container.style.cursor = this.originalContainerCursor, this.container.classList.remove("supermouse-scope", this.scopeClass, this.hideClass), this.container !== document.body && this.originalContainerPosition === "static" && (this.container.style.position = "");
92
+ this._detach();
79
93
  }
80
94
  }
81
- class I {
95
+ class C {
82
96
  constructor(t, e) {
83
- this.config = t, this.name = t.name, this.cursorMode = t.cursor ?? e.cursor, this.hoverSelectors = new Set(t.hoverSelectors ?? e.hoverSelectors), this.inheritDataAttributes = t.inheritDataAttributes ?? e.inheritDataAttributes;
84
- const s = t.cursorPolicy ? x(t.cursorPolicy) : e.cursorPolicy, n = T(s);
85
- this.nativeSelectors = n.native, this.hideSelectors = n.hide, this.stage = new A(t.container, t.zIndex ?? e.zIndex);
97
+ this.config = t, this.name = t.name, this.cursorMode = t.cursor ?? e.cursor, this.hoverSelectors = new Set(t.hoverSelectors ?? e.hoverSelectors), this.inheritDataAttributes = t.inheritDataAttributes ?? e.inheritDataAttributes, this.ruleEntries = t.rules ? Object.entries(t.rules) : e.ruleEntries;
98
+ const s = t.cursorPolicy ? b(t.cursorPolicy) : e.cursorPolicy;
99
+ this.nativeSelectors = s.native, this.hideSelectors = s.hide;
100
+ let i;
101
+ typeof t.container == "string" ? (this.containerSelector = t.container, this._bound = !1, i = document.createElement("div"), i.setAttribute("data-supermouse-placeholder", "")) : (this.containerSelector = null, this._bound = !0, i = t.container), this.stage = new P(i, t.zIndex ?? e.zIndex);
86
102
  }
87
103
  config;
88
104
  stage;
105
+ /**
106
+ * The CSS selector used for lazy resolution, or null for eager scopes.
107
+ * Derived from `config.container` at construction: a string means lazy,
108
+ * an element means eager.
109
+ */
110
+ containerSelector;
89
111
  hoverSelectors;
90
112
  plugins = [];
91
113
  nativeSelectors;
92
114
  hideSelectors;
93
115
  inheritDataAttributes;
116
+ ruleEntries;
94
117
  name;
95
118
  cursorMode;
96
- contains(t) {
97
- return this.stage.containerElement.contains(t);
98
- }
119
+ active = !0;
120
+ _bound;
99
121
  get container() {
100
122
  return this.stage.containerElement;
101
123
  }
124
+ /**
125
+ * True for eager scopes, and for selector scopes that have matched a
126
+ * live element at least once.
127
+ */
128
+ get resolved() {
129
+ return this._bound;
130
+ }
131
+ /**
132
+ * True if this scope owns the given element. Element equality for eager
133
+ * scopes; `Element.matches` for selector scopes. Invalid selectors are
134
+ * treated as non-matching; validation happens once at scope creation.
135
+ */
136
+ match(t) {
137
+ if (this.containerSelector === null)
138
+ return this.stage.containerElement === t;
139
+ try {
140
+ return t.matches(this.containerSelector);
141
+ } catch {
142
+ return !1;
143
+ }
144
+ }
145
+ /**
146
+ * Rebinds this scope's stage to a matched element. No-op for eager
147
+ * scopes, and no-op if the stage is already attached to `el`.
148
+ */
149
+ bind(t) {
150
+ this.containerSelector !== null && (this._bound && this.stage.containerElement === t || (this.stage.attach(t), this._bound = !0));
151
+ }
152
+ contains(t) {
153
+ return this._bound && this.stage.containerElement.contains(t);
154
+ }
102
155
  get hoverSelectorString() {
103
156
  return Array.from(this.hoverSelectors).join(", ");
104
157
  }
105
158
  get nativeSelectorString() {
106
159
  return this.nativeSelectors.join(", ");
107
160
  }
108
- /** Build the CSS rules this scope contributes to the shared stylesheet. */
109
161
  buildRules() {
110
162
  const t = this.stage.getRulePrefix(), e = this.stage.getExclusion(), s = [
111
163
  `${t}${e} { cursor: none !important; }`,
112
164
  `${t} *${e} { cursor: none !important; }`
113
165
  ];
114
- for (const n of this.hideSelectors)
115
- s.push(`${t} ${n}${e} { cursor: none !important; }`);
116
- return s.push(`${t} label${e} { cursor: none !important; }`), s.push(`${t} select${e} { cursor: none !important; }`), s.push(
166
+ for (const i of this.hideSelectors)
167
+ s.push(`${t} ${i}${e} { cursor: none !important; }`);
168
+ return s.push(
117
169
  `${t} input[type="range"]${e}::-webkit-slider-thumb { cursor: none !important; }`
118
170
  ), s.push(
119
171
  `${t} input[type="range"]${e}::-moz-range-thumb { cursor: none !important; }`
120
172
  ), s;
121
173
  }
122
174
  }
123
- const f = { x: -100, y: -100 }, R = /* @__PURE__ */ new Set([
175
+ const d = { x: -100, y: -100 }, E = /* @__PURE__ */ new Set([
124
176
  "default",
125
177
  "auto",
126
178
  "pointer",
@@ -128,60 +180,24 @@ const f = { x: -100, y: -100 }, R = /* @__PURE__ */ new Set([
128
180
  "inherit",
129
181
  "grab",
130
182
  "grabbing"
131
- ]), D = [
183
+ ]), _ = [
132
184
  "a",
133
185
  "button",
134
186
  "input",
135
187
  "textarea",
136
188
  "[data-hover]",
137
189
  "[data-cursor]"
138
- ], u = /* @__PURE__ */ new Map(), _ = /* @__PURE__ */ new Map();
139
- let v = null, g = !1, m = !1, S = 0;
140
- function C(i) {
141
- const t = i.getBoundingClientRect();
142
- return { left: t.left, top: t.top, width: t.width, height: t.height };
143
- }
144
- function M(i) {
145
- u.set(i, C(i));
146
- const t = _.get(i);
147
- if (t) for (const e of t) e();
148
- }
149
- function y() {
150
- for (const i of u.keys()) M(i);
151
- }
152
- function O() {
153
- v || typeof ResizeObserver > "u" || (v = new ResizeObserver((i) => {
154
- for (const t of i) {
155
- const e = t.target;
156
- u.has(e) && M(e);
157
- }
158
- }));
159
- }
160
- function $() {
161
- typeof window > "u" || (g || (window.addEventListener("scroll", y, { passive: !0, capture: !0 }), g = !0), m || (window.addEventListener("resize", y, { passive: !0 }), m = !0));
162
- }
163
- function z(i, t) {
164
- return S++, u.has(i) || (u.set(i, C(i)), O(), v?.observe(i), $()), () => {
165
- S--, S === 0 && (v?.disconnect(), v = null, u.clear(), _.clear(), g && (window.removeEventListener("scroll", y, { capture: !0 }), g = !1), m && (window.removeEventListener("resize", y), m = !1));
166
- };
167
- }
168
- function k(i) {
169
- let t = u.get(i);
170
- return t || (t = C(i), u.set(i, t)), t;
171
- }
172
- function N(i, t, e) {
173
- if (i === document.body) return { x: t, y: e };
174
- const s = k(i);
175
- return { x: t - s.left, y: e - s.top };
176
- }
177
- class B {
178
- constructor(t, e, s, n) {
179
- this.state = t, this.options = e, this.onEnableChange = s, this.onActiveScopeChange = n, this.dataPrefix = e.dataPrefix ?? "supermouse", this.normalizedDataPrefix = this.dataPrefix.toLowerCase(), this.ignoreAttribute = `data-${this.dataPrefix}-ignore`, this.ruleEntries = e.rules ? Object.entries(e.rules) : [], this.checkDeviceCapability(), this.checkMotionPreference(), this.bindEvents();
190
+ ];
191
+ class w {
192
+ constructor(t, e, s, i, h, r) {
193
+ this.state = t, this.options = e, this.onEnableChange = s, this.onActiveScopeChange = i, this.resolveScope = h, this.onHoverSettled = r, this.dataPrefix = e.dataPrefix ?? "supermouse", this.normalizedDataPrefix = this.dataPrefix.toLowerCase(), this.ignoreAttribute = `data-${this.dataPrefix}-ignore`, this.checkDeviceCapability(), this.checkMotionPreference(), this.bindEvents();
180
194
  }
181
195
  state;
182
196
  options;
183
197
  onEnableChange;
184
198
  onActiveScopeChange;
199
+ resolveScope;
200
+ onHoverSettled;
185
201
  abortController = new AbortController();
186
202
  mediaQueryList;
187
203
  motionQuery;
@@ -189,37 +205,26 @@ class B {
189
205
  normalizedDataPrefix;
190
206
  ignoreAttribute;
191
207
  activeScope = null;
192
- scopeByContainer = /* @__PURE__ */ new Map();
193
208
  nativeTarget = null;
194
209
  currentTarget = null;
195
210
  lastParsedTarget = null;
196
- ruleEntries;
197
- releaseViewport = null;
198
211
  hasSeenPointer = !1;
199
212
  isEnabled = !0;
200
213
  viewportX = 0;
201
214
  viewportY = 0;
202
215
  /** Cached matched rules per ancestor. Recomputed only when the hover target changes. */
203
216
  cachedChain = [];
204
- setScopes(t) {
205
- this.scopeByContainer.clear();
206
- for (const e of t) this.scopeByContainer.set(e.container, e);
207
- }
208
217
  setActiveScope(t) {
209
- t !== this.activeScope && (this.activeScope = t, this.releaseViewport?.(), this.releaseViewport = t && t.container !== document.body ? z(t.container) : null, this.hasSeenPointer && (this.applyPointerToState(), this.state.target.x = this.state.smooth.x = this.state.pointer.x, this.state.target.y = this.state.smooth.y = this.state.pointer.y), this.onActiveScopeChange(t));
210
- }
211
- findScope(t) {
212
- let e = t;
213
- for (; e; ) {
214
- const s = this.scopeByContainer.get(e);
215
- if (s) return s;
216
- e = e.parentElement;
217
- }
218
- return null;
218
+ t !== this.activeScope && (this.activeScope = t, this.lastParsedTarget = null, this.cachedChain = [], this.hasSeenPointer && (this.applyPointerToState(), this.state.target.x = this.state.smooth.x = this.state.pointer.x, this.state.target.y = this.state.smooth.y = this.state.pointer.y), this.onActiveScopeChange(t));
219
219
  }
220
220
  applyPointerToState() {
221
- const t = this.activeScope?.container ?? document.body, e = N(t, this.viewportX, this.viewportY);
222
- this.state.pointer.x = e.x, this.state.pointer.y = e.y;
221
+ const t = this.activeScope?.container ?? document.body;
222
+ if (t === document.body) {
223
+ this.state.pointer.x = this.viewportX, this.state.pointer.y = this.viewportY;
224
+ return;
225
+ }
226
+ const e = t.getBoundingClientRect();
227
+ this.state.pointer.x = this.viewportX - e.left, this.state.pointer.y = this.viewportY - e.top;
223
228
  }
224
229
  checkDeviceCapability() {
225
230
  this.options.autoDisableOnMobile && (this.mediaQueryList = window.matchMedia("(pointer: fine)"), this.updateEnabledState(this.mediaQueryList.matches), this.mediaQueryList.addEventListener("change", (t) => this.updateEnabledState(t.matches), {
@@ -239,40 +244,42 @@ class B {
239
244
  this.isEnabled = t, this.onEnableChange(t);
240
245
  }
241
246
  parseDOMInteraction(t) {
242
- const e = this.activeScope?.container ?? document.body, s = this.activeScope?.inheritDataAttributes ?? !0, n = this.normalizedDataPrefix;
247
+ if (!this.activeScope) return;
248
+ const e = this.activeScope.container, s = this.activeScope.inheritDataAttributes, i = this.normalizedDataPrefix, h = this.activeScope.ruleEntries;
243
249
  if (t !== this.lastParsedTarget) {
244
250
  this.lastParsedTarget = t, this.cachedChain = [];
245
251
  let o = t;
246
252
  for (; o; ) {
247
- const d = [];
248
- for (const [a, h] of this.ruleEntries)
249
- this.matchesSelector(o, a) && d.push({ selector: a, rules: h });
250
- if (this.cachedChain.push({ element: o, matchedRules: d }), !s || o === e) break;
253
+ const p = [];
254
+ for (const [a, l] of h)
255
+ this.matchesSelector(o, a) && p.push({ selector: a, rules: l });
256
+ if (this.cachedChain.push({ element: o, matchedRules: p }), !s || o === e) break;
251
257
  const c = o.parentElement;
252
258
  if (!c || !e.contains(c)) break;
253
259
  o = c;
254
260
  }
255
261
  }
256
262
  const r = {};
257
- for (const { element: o, matchedRules: d } of this.cachedChain) {
263
+ for (const { element: o, matchedRules: p } of this.cachedChain) {
258
264
  const c = {};
259
- for (const { rules: a } of d)
265
+ for (const { rules: a } of p)
260
266
  try {
261
- const h = typeof a == "function" ? a(o) : a;
262
- if (!h || typeof h != "object") continue;
263
- for (const [b, p] of Object.entries(h))
264
- c[b] = typeof p == "function" ? p(o) : p;
265
- } catch {
267
+ const l = typeof a == "function" ? a(o) : a;
268
+ if (!l || typeof l != "object") continue;
269
+ for (const [v, u] of Object.entries(l))
270
+ c[v] = typeof u == "function" ? u(o) : u;
271
+ } catch (l) {
272
+ console.error("[Supermouse] Rule threw during evaluation:", l);
266
273
  }
267
274
  for (const a in o.dataset) {
268
- if (!a.toLowerCase().startsWith(n)) continue;
269
- const h = a.slice(n.length);
270
- if (!h) continue;
271
- const b = h[0].toLowerCase() + h.slice(1), p = o.dataset[a];
272
- c[b] = p == null || p === "" ? !0 : p;
275
+ if (!a.toLowerCase().startsWith(i)) continue;
276
+ const l = a.slice(i.length);
277
+ if (!l) continue;
278
+ const v = l[0].toLowerCase() + l.slice(1), u = o.dataset[a];
279
+ c[v] = u == null || u === "" ? !0 : u;
273
280
  }
274
- for (const [a, h] of Object.entries(c))
275
- a in r || (r[a] = h);
281
+ for (const [a, l] of Object.entries(c))
282
+ a in r || (r[a] = l);
276
283
  }
277
284
  this.state.interaction = r;
278
285
  }
@@ -297,26 +304,33 @@ class B {
297
304
  };
298
305
  handleMouseOver = (t) => {
299
306
  if (!this.isEnabled) return;
300
- const e = t.target, s = this.findScope(e);
301
- if (s && s !== this.activeScope && this.setActiveScope(s), !this.activeScope) return;
302
- if (this.state.cursorMode === "auto" && e.closest(`[${this.ignoreAttribute}]`)) {
303
- this.clearHover(), this.state.isNative = !0, this.nativeTarget = e;
307
+ const e = t.target, s = this.resolveScope(e);
308
+ s && s !== this.activeScope && this.setActiveScope(s), this.activeScope && (this.settleHoverState(e), this.onHoverSettled());
309
+ };
310
+ isPointerInside(t) {
311
+ if (!this.hasSeenPointer) return !1;
312
+ const e = t.getBoundingClientRect();
313
+ return this.viewportX >= e.left && this.viewportX <= e.right && this.viewportY >= e.top && this.viewportY <= e.bottom;
314
+ }
315
+ settleHoverState(t) {
316
+ if (this.state.cursorMode === "auto" && t.closest(`[${this.ignoreAttribute}]`)) {
317
+ this.clearHover(), this.state.isNative = !0, this.nativeTarget = t;
304
318
  return;
305
319
  }
306
- this.state.isNative = !1, this.nativeTarget = null, this.currentTarget = e, this.parseDOMInteraction(e);
307
- const n = e.closest(this.activeScope.hoverSelectorString);
308
- if (n && (this.state.isHover = !0, this.state.hoverTarget = n), this.state.cursorMode === "auto") {
309
- if (this.matchesSelector(e, this.activeScope.nativeSelectorString)) {
310
- this.state.isNative = !0, this.nativeTarget = e;
320
+ this.state.isNative = !1, this.nativeTarget = null, this.currentTarget = t, this.parseDOMInteraction(t);
321
+ const e = t.closest(this.activeScope.hoverSelectorString);
322
+ if (e && (this.state.isHover = !0, this.state.hoverTarget = e), this.state.cursorMode === "auto") {
323
+ if (this.matchesSelector(t, this.activeScope.nativeSelectorString)) {
324
+ this.state.isNative = !0, this.nativeTarget = t;
311
325
  return;
312
326
  }
313
- (e.isContentEditable || !R.has(this.resolveComputedCursor(e))) && (this.state.isNative = !0, this.nativeTarget = e);
327
+ (t.isContentEditable || !E.has(this.resolveComputedCursor(t))) && (this.state.isNative = !0, this.nativeTarget = t);
314
328
  }
315
- };
329
+ }
316
330
  handleMouseOut = (t) => {
317
331
  if (!this.isEnabled) return;
318
332
  const e = t.target, s = t.relatedTarget;
319
- (e === this.state.hoverTarget || e.contains(this.state.hoverTarget)) && (!s || !this.state.hoverTarget?.contains(s)) && (this.state.isHover = !1, this.state.hoverTarget = null), this.nativeTarget && (e === this.nativeTarget || e.contains(this.nativeTarget)) && (!s || !this.nativeTarget.contains(s)) && (this.state.isNative = !1, this.nativeTarget = null), e === this.currentTarget && (this.currentTarget = null, this.lastParsedTarget = null, this.cachedChain = []), !s && this.options.hideOnLeave && (this.state.hasReceivedInput = !1, this.state.pointer = { ...f });
333
+ (e === this.state.hoverTarget || e.contains(this.state.hoverTarget)) && (!s || !this.state.hoverTarget?.contains(s)) && (this.state.isHover = !1, this.state.hoverTarget = null), this.nativeTarget && (e === this.nativeTarget || e.contains(this.nativeTarget)) && (!s || !this.nativeTarget.contains(s)) && (this.state.isNative = !1, this.nativeTarget = null), e === this.currentTarget && (this.currentTarget = null, this.lastParsedTarget = null, this.cachedChain = []), !s && this.options.hideOnLeave && (this.state.hasReceivedInput = !1, this.state.pointer = { ...d });
320
334
  };
321
335
  clearHover() {
322
336
  this.state.isHover = !1, this.state.hoverTarget = null, this.state.isNative = !1, this.nativeTarget = null, this.state.interaction = {}, this.currentTarget = null, this.lastParsedTarget = null, this.cachedChain = [];
@@ -329,37 +343,41 @@ class B {
329
343
  window.addEventListener("pointermove", this.handleMove, { passive: !0, signal: t }), window.addEventListener("pointerdown", this.handleDown, { passive: !0, signal: t }), window.addEventListener("pointerup", this.handleUp, { signal: t }), document.addEventListener("mouseover", this.handleMouseOver, { signal: t }), document.addEventListener("mouseout", this.handleMouseOut, { signal: t });
330
344
  }
331
345
  destroy() {
332
- this.abortController.abort(), this.releaseViewport?.(), this.cachedChain = [];
346
+ this.abortController.abort(), this.cachedChain = [];
333
347
  }
334
348
  }
335
- let l = null;
336
- function H() {
337
- l || typeof document > "u" || (l = document.createElement("style"), l.id = "supermouse-styles", document.head.appendChild(l));
349
+ let x = 0;
350
+ const f = /* @__PURE__ */ new Map();
351
+ function M() {
352
+ return x++;
338
353
  }
339
- function V(i) {
340
- H(), l && (l.textContent = i.join(`
341
- `));
354
+ function T(n, t) {
355
+ let e = f.get(n);
356
+ e || (e = document.createElement("style"), e.id = `supermouse-styles-${n}`, document.head.appendChild(e), f.set(n, e)), e.textContent = t.join(`
357
+ `);
342
358
  }
343
- function F() {
344
- l?.remove(), l = null;
359
+ function A(n) {
360
+ f.get(n)?.remove(), f.delete(n);
345
361
  }
346
- const E = "2.5.0-beta.1";
347
- function j(i, t, e) {
348
- return i + (t - i) * e;
362
+ const g = "2.5.0-beta.3";
363
+ function D(n, t, e) {
364
+ return n + (t - n) * e;
349
365
  }
350
- function P(i, t, e, s) {
351
- return j(i, t, 1 - Math.exp(-e * s));
366
+ function m(n, t, e, s) {
367
+ return D(n, t, 1 - Math.exp(-e * s));
352
368
  }
353
- class Q {
354
- static version = E;
355
- version = E;
369
+ class I {
370
+ static version = g;
371
+ version = g;
356
372
  state;
357
373
  options;
358
374
  _scopes = [];
359
375
  _activeScope = null;
360
376
  _installingScope = null;
361
- scopeByContainer = /* @__PURE__ */ new Map();
377
+ scopeByName = /* @__PURE__ */ new Map();
378
+ scopeHandles = /* @__PURE__ */ new Map();
362
379
  input;
380
+ styleOwner;
363
381
  rafId = 0;
364
382
  lastTime = 0;
365
383
  visibilityAbortController = new AbortController();
@@ -377,10 +395,10 @@ class Q {
377
395
  zIndex: 9999,
378
396
  inheritDataAttributes: !0,
379
397
  ...t
380
- }, this.state = {
381
- pointer: { ...f },
382
- target: { ...f },
383
- smooth: { ...f },
398
+ }, this.styleOwner = M(), this.state = {
399
+ pointer: { ...d },
400
+ target: { ...d },
401
+ smooth: { ...d },
384
402
  velocity: { x: 0, y: 0 },
385
403
  displacement: { x: 0, y: 0 },
386
404
  angle: 0,
@@ -392,7 +410,8 @@ class Q {
392
410
  reducedMotion: !1,
393
411
  hasReceivedInput: !1,
394
412
  shape: null,
395
- interaction: {}
413
+ interaction: {},
414
+ scope: null
396
415
  };
397
416
  const e = this.createScope({
398
417
  container: this.options.container,
@@ -400,16 +419,19 @@ class Q {
400
419
  hoverSelectors: this.options.hoverSelectors,
401
420
  cursorPolicy: this.options.cursorPolicy,
402
421
  plugins: this.options.plugins,
422
+ rules: this.options.rules,
403
423
  zIndex: this.options.zIndex
404
424
  });
405
- if (this.input = new B(
425
+ if (this.input = new w(
406
426
  this.state,
407
427
  this.options,
408
428
  (s) => {
409
429
  s || this.reset();
410
430
  },
411
- (s) => this.handleActiveScopeChange(s)
412
- ), this.input.setScopes(this._scopes), this.input.setActiveScope(e), this.installPlugins(e), this.options.scopes)
431
+ (s) => this.handleActiveScopeChange(s),
432
+ (s) => this.resolveScopeForNode(s),
433
+ () => this.applyCursorNow()
434
+ ), this.input.setActiveScope(e), this.installPlugins(e), this.options.scopes)
413
435
  for (const s of this.options.scopes) this.addScope(s);
414
436
  this.rebuildStylesheet(), this.bindVisibilityHandling(), this.options.autoStart && this.startLoop();
415
437
  }
@@ -426,38 +448,72 @@ class Q {
426
448
  get isRunning() {
427
449
  return this._running;
428
450
  }
451
+ /**
452
+ * Installs a plugin into the primary scope.
453
+ *
454
+ * In multi-scope mode, prefer `handle.use(plugin)` to target a specific
455
+ * scope. This method always installs to the primary scope, regardless of
456
+ * which scope is currently active.
457
+ */
429
458
  use(t) {
430
459
  return this.installPlugin(this._scopes[0], t), this;
431
460
  }
461
+ /**
462
+ * Searches every scope and returns the first plugin matching `name`.
463
+ * Scopes are searched in registration order (primary first).
464
+ */
432
465
  getPlugin(t) {
433
466
  for (const e of this._scopes) {
434
- const s = e.plugins.find((n) => n.name === t);
467
+ const s = e.plugins.find((i) => i.name === t);
435
468
  if (s) return s;
436
469
  }
437
470
  }
471
+ getScope(t) {
472
+ const e = this.scopeByName.get(t);
473
+ return e ? this.scopeHandles.get(e) : void 0;
474
+ }
475
+ /**
476
+ * Enables a plugin by name, wherever it lives.
477
+ *
478
+ * If the plugin's scope is currently active, the activation lifecycle
479
+ * runs immediately. If the scope is inactive, `isEnabled` is set but
480
+ * hooks do not fire until the scope next activates.
481
+ */
438
482
  enablePlugin(t) {
439
483
  const e = this.getPlugin(t);
440
- e && e.isEnabled === !1 && (e.isEnabled = !0, e.element && (e.element.style.display = ""), e.onEnable?.(this));
484
+ if (!e || e.isEnabled !== !1) return;
485
+ e.isEnabled = !0;
486
+ const s = this.findScopeForPlugin(e);
487
+ s && s === this._activeScope && s.active && this.scopeActivatePlugin(e);
441
488
  }
489
+ /**
490
+ * Disables a plugin by name, wherever it lives.
491
+ *
492
+ * If the plugin's scope is currently active, the deactivation lifecycle
493
+ * runs immediately. If the scope is inactive, `isEnabled` is set but
494
+ * hooks do not fire — they already ran when the scope went inactive.
495
+ */
442
496
  disablePlugin(t) {
443
497
  const e = this.getPlugin(t);
444
- e && e.isEnabled !== !1 && this.deactivatePlugin(e);
498
+ if (!e || e.isEnabled === !1) return;
499
+ const s = this.findScopeForPlugin(e);
500
+ s !== null && s === this._activeScope && s.active ? this.deactivatePlugin(e) : e.isEnabled = !1;
445
501
  }
446
502
  togglePlugin(t) {
447
503
  const e = this.getPlugin(t);
448
504
  e && (e.isEnabled === !1 ? this.enablePlugin(t) : this.disablePlugin(t));
449
505
  }
506
+ /**
507
+ * Sets the cursor mode on the *active* scope.
508
+ *
509
+ * To set the mode on a specific scope, use `handle.setCursor(mode)`.
510
+ */
450
511
  setCursor(t) {
451
- this._activeScope && (this._activeScope.cursorMode = t, this.state.cursorMode = t);
512
+ this._activeScope && this.setScopeCursor(this._activeScope, t);
452
513
  }
453
514
  addScope(t) {
454
515
  const e = this.createScope(t);
455
- return this.input.setScopes(this._scopes), this.installPlugins(e), this.rebuildStylesheet(), {
456
- name: e.name,
457
- container: e.container,
458
- remove: () => this.removeScope(e),
459
- setCursor: (s) => this.setScopeCursor(e, s)
460
- };
516
+ return this.installPlugins(e), this.rebuildStylesheet(), this.scopeHandles.get(e);
461
517
  }
462
518
  enable() {
463
519
  this.input.isEnabled = !0, this.input.hasSeenPointer && (this.state.target.x = this.state.smooth.x = this.state.pointer.x, this.state.target.y = this.state.smooth.y = this.state.pointer.y, this.resetMotion(), this.state.hasReceivedInput = !0), this._activeScope && this._activeScope.stage.setNativeCursor(this.resolveCursorState());
@@ -466,7 +522,7 @@ class Q {
466
522
  this.input.isEnabled = !1, this._activeScope && (this._activeScope.stage.setNativeCursor("auto"), this._activeScope.stage.setVisibility(!1)), t?.reset && this.reset();
467
523
  }
468
524
  reset() {
469
- this.state.target = { ...f }, this.state.smooth = { ...f }, this.resetMotion(), this.state.angle = 0, this.state.hasReceivedInput = !1, this.state.shape = null, this.state.interaction = {};
525
+ this.state.target = { ...d }, this.state.smooth = { ...d }, this.resetMotion(), this.state.angle = 0, this.state.hasReceivedInput = !1, this.state.shape = null, this.state.interaction = {};
470
526
  }
471
527
  start() {
472
528
  this.startLoop();
@@ -480,89 +536,229 @@ class Q {
480
536
  for (const e of t.plugins)
481
537
  try {
482
538
  e.destroy?.(this);
483
- } catch {
539
+ } catch (s) {
540
+ console.error(s);
484
541
  }
485
542
  t.stage.destroy();
486
543
  }
487
- this._scopes = [], this.scopeByContainer.clear(), this._activeScope = null, F();
544
+ this._scopes = [], this.scopeByName.clear(), this.scopeHandles.clear(), this._activeScope = null, this.state.scope = null, A(this.styleOwner);
488
545
  }
489
546
  // ─── Internal ───
490
547
  _running = !1;
491
548
  createScope(t) {
492
- const e = new I(t, {
549
+ if (typeof t.container != "string") {
550
+ const s = t.container;
551
+ for (const i of this._scopes)
552
+ if (i.containerSelector === null && i.container === s) {
553
+ console.warn(
554
+ "[Supermouse] A scope is already registered for this container. The previous scope will no longer activate."
555
+ );
556
+ break;
557
+ }
558
+ }
559
+ const e = new C(t, {
493
560
  cursor: this.options.cursor,
494
- hoverSelectors: this.options.hoverSelectors ?? D,
495
- cursorPolicy: this.options.cursorPolicy ? x(this.options.cursorPolicy) : w,
561
+ hoverSelectors: this.options.hoverSelectors ?? _,
562
+ cursorPolicy: this.options.cursorPolicy ? b(this.options.cursorPolicy) : S,
496
563
  zIndex: this.options.zIndex,
497
- inheritDataAttributes: this.options.inheritDataAttributes
564
+ inheritDataAttributes: this.options.inheritDataAttributes,
565
+ ruleEntries: this.options.rules ? Object.entries(this.options.rules) : []
498
566
  });
499
- return this._scopes.push(e), this.scopeByContainer.set(e.container, e), e;
567
+ return this._scopes.push(e), e.name && this.scopeByName.set(e.name, e), this.scopeHandles.set(e, this.buildScopeHandle(e)), e;
568
+ }
569
+ buildScopeHandle(t) {
570
+ const e = {
571
+ get name() {
572
+ return t.name;
573
+ },
574
+ get container() {
575
+ return t.resolved ? t.container : null;
576
+ },
577
+ get active() {
578
+ return t.active;
579
+ },
580
+ get resolved() {
581
+ return t.resolved;
582
+ },
583
+ destroy: () => this.destroyScope(t),
584
+ setCursor: (s) => this.setScopeCursor(t, s),
585
+ deactivate: () => this.deactivateScope(t),
586
+ activate: () => this.activateScope(t),
587
+ use: (s) => (this.installPlugin(t, s), e),
588
+ removePlugin: (s) => this.removePluginFromScope(t, s),
589
+ getPlugin: (s) => t.plugins.find((i) => i.name === s)
590
+ };
591
+ return e;
500
592
  }
501
- removeScope(t) {
593
+ destroyScope(t) {
502
594
  const e = this._scopes.indexOf(t);
503
595
  if (e !== -1) {
504
596
  for (const s of t.plugins)
505
597
  try {
506
598
  s.destroy?.(this);
507
- } catch {
599
+ } catch (i) {
600
+ console.error(i);
508
601
  }
509
- t.plugins.length = 0, t.stage.destroy(), this._scopes.splice(e, 1), this.scopeByContainer.delete(t.container), this.input.setScopes(this._scopes), this._activeScope === t && this.input.setActiveScope(this._scopes[0] ?? null), this.rebuildStylesheet();
602
+ if (t.plugins.length = 0, t.stage.destroy(), this._scopes.splice(e, 1), t.name && this.scopeByName.delete(t.name), this.scopeHandles.delete(t), this._activeScope === t) {
603
+ const s = this.resolveScopeForNode(t.container.parentElement) ?? this._scopes.find((i) => i.active) ?? null;
604
+ this.input.setActiveScope(s), this.input.clearHover(), this.applyCursorNow();
605
+ }
606
+ this.rebuildStylesheet();
510
607
  }
511
608
  }
512
609
  setScopeCursor(t, e) {
513
- t.cursorMode = e, t === this._activeScope && (this.state.cursorMode = e);
610
+ t.cursorMode = e, t === this._activeScope && (this.state.cursorMode = e, this.applyCursorNow());
611
+ }
612
+ /**
613
+ * Marks the scope ineligible for activation and, if it was active, yields
614
+ * immediately to the nearest active ancestor scope (or nothing).
615
+ */
616
+ deactivateScope(t) {
617
+ if (t.active && (t.active = !1, this._activeScope === t)) {
618
+ const e = this.resolveScopeForNode(t.container.parentElement) ?? this._scopes.find((s) => s.active) ?? null;
619
+ this.input.setActiveScope(e), this.input.clearHover(), this.applyCursorNow();
620
+ }
621
+ }
622
+ /**
623
+ * Marks the scope eligible for activation.
624
+ *
625
+ * If the pointer is already inside the scope's container, the scope
626
+ * becomes active immediately. Otherwise it activates on the next
627
+ * `mouseover` inside the container. Deactivation is always eager.
628
+ */
629
+ activateScope(t) {
630
+ if (t.active = !0, this._activeScope !== t && this.input.hasSeenPointer) {
631
+ if (t.containerSelector !== null && !t.resolved) {
632
+ const e = document.querySelectorAll(t.containerSelector);
633
+ for (const s of e)
634
+ if (this.input.isPointerInside(s)) {
635
+ t.bind(s);
636
+ break;
637
+ }
638
+ }
639
+ t.resolved && this.input.isPointerInside(t.container) && (this.input.setActiveScope(t), this.applyCursorNow());
640
+ }
641
+ }
642
+ /**
643
+ * Called by `Input` on every `mouseover`, and internally when the active
644
+ * scope is removed or deactivated. Walks from `node` up the ancestor
645
+ * chain, returning the innermost active scope whose `match` succeeds
646
+ * for an ancestor. The matched scope is bound to that ancestor before
647
+ * return.
648
+ *
649
+ * The walk order is what enforces "innermost wins": the first ancestor
650
+ * matching any active scope is, by construction, the closest one to the
651
+ * event target. Detached containers can never be ancestors of a live
652
+ * event target, so no cleanup of stale bindings is required here.
653
+ */
654
+ resolveScopeForNode(t) {
655
+ for (let e = t; e; e = e.parentElement)
656
+ for (const s of this._scopes)
657
+ if (s.active && s.match(e))
658
+ return s.bind(e), s;
659
+ return null;
660
+ }
661
+ /**
662
+ * Synchronously writes the current cursor and stage visibility for the
663
+ * active scope. Called after hover state settles, on cursor mode changes,
664
+ * and on programmatic scope transitions. The `update()` rAF path calls
665
+ * the same writers as a backstop.
666
+ */
667
+ applyCursorNow() {
668
+ const t = this._activeScope;
669
+ t && (t.stage.setVisibility(this.resolveStageVisibility()), this.input.isEnabled && t.stage.setNativeCursor(this.resolveCursorState()));
670
+ }
671
+ findScopeForPlugin(t) {
672
+ for (const e of this._scopes)
673
+ if (e.plugins.includes(t)) return e;
674
+ return null;
514
675
  }
515
676
  installPlugins(t) {
516
677
  if (t.config.plugins)
517
678
  for (const e of t.config.plugins) this.installPlugin(t, e);
518
679
  }
519
680
  installPlugin(t, e) {
520
- if (!t.plugins.some((s) => s.name === e.name)) {
521
- e.isEnabled ??= !0, this._installingScope = t;
681
+ if (t.plugins.some((s) => s.name === e.name)) {
682
+ console.warn(`[Supermouse] Plugin "${e.name}" already installed.`);
683
+ return;
684
+ }
685
+ e.isEnabled ??= !0, this._installingScope = t;
686
+ try {
687
+ e.install?.(this);
688
+ } catch (s) {
689
+ console.error(`[Supermouse] Failed to install plugin '${e.name}'.`, s), this._installingScope = null;
690
+ return;
691
+ }
692
+ this._installingScope = null, t.plugins.push(e), t.plugins.sort((s, i) => (s.priority ?? 0) - (i.priority ?? 0)), (t !== this._activeScope || !t.active) && e.element && (e.element.style.display = "none");
693
+ }
694
+ removePluginFromScope(t, e) {
695
+ const s = t.plugins.findIndex((o) => o.name === e);
696
+ if (s === -1) return;
697
+ const i = t.plugins[s];
698
+ t.plugins.splice(s, 1);
699
+ const h = () => {
522
700
  try {
523
- e.install?.(this);
524
- } catch {
525
- this._installingScope = null;
526
- return;
701
+ i.onDisable?.(this), i.destroy?.(this);
702
+ } catch (o) {
703
+ console.error(`[Supermouse] Plugin '${e}' cleanup threw:`, o);
527
704
  }
528
- this._installingScope = null, t.plugins.push(e), t.plugins.sort((s, n) => (s.priority ?? 0) - (n.priority ?? 0)), t !== this._activeScope && (e.isEnabled = !1, e.element && (e.element.style.display = "none"));
529
- }
705
+ i.element?.remove();
706
+ }, r = i.onBeforeDisable?.(this);
707
+ r && typeof r.then == "function" ? Promise.resolve(r).then(h).catch((o) => {
708
+ console.error(`[Supermouse] Plugin '${e}' onBeforeDisable threw:`, o), h();
709
+ }) : h();
530
710
  }
531
711
  handleActiveScopeChange(t) {
532
712
  const e = this._activeScope;
533
713
  if (e !== t) {
534
714
  if (e)
535
- for (const s of e.plugins) this.deactivatePlugin(s);
715
+ for (const s of e.plugins)
716
+ s.isEnabled !== !1 && this.scopeDeactivatePlugin(s);
536
717
  if (this._activeScope = t, t) {
537
- this.state.cursorMode = t.cursorMode;
538
- for (const s of t.plugins) this.activatePlugin(s);
539
- }
718
+ this.state.cursorMode = t.cursorMode, this.state.scope = { name: t.name, container: t.container };
719
+ for (const s of t.plugins)
720
+ s.isEnabled !== !1 && this.scopeActivatePlugin(s);
721
+ } else
722
+ this.state.scope = null;
540
723
  }
541
724
  }
542
- activatePlugin(t) {
543
- t.isEnabled === !1 && (t.isEnabled = !0, t.element && (t.element.style.display = ""), t.onEnable?.(this));
544
- }
545
725
  deactivatePlugin(t) {
546
726
  if (t.isEnabled === !1) return;
547
727
  t.isEnabled = !1;
548
728
  const e = () => {
549
729
  t.element && (t.element.style.display = "none"), t.onDisable?.(this);
550
730
  }, s = t.onBeforeDisable?.(this);
551
- s && typeof s.then == "function" ? Promise.resolve(s).then(e).catch((n) => {
552
- e();
731
+ s && typeof s.then == "function" ? Promise.resolve(s).then(e).catch((i) => {
732
+ console.error(`[Supermouse] Plugin '${t.name}' onBeforeDisable threw:`, i), e();
733
+ }) : e();
734
+ }
735
+ /**
736
+ * Runs the deactivation lifecycle for a plugin whose owning scope just
737
+ * became inactive. Does NOT flip `plugin.isEnabled`, so user intent
738
+ * (e.g. States disabling a plugin) survives the scope round-trip.
739
+ */
740
+ scopeDeactivatePlugin(t) {
741
+ const e = () => {
742
+ t.element && (t.element.style.display = "none"), t.onDisable?.(this);
743
+ }, s = t.onBeforeDisable?.(this);
744
+ s && typeof s.then == "function" ? Promise.resolve(s).then(e).catch((i) => {
745
+ console.error(`[Supermouse] Plugin '${t.name}' onBeforeDisable threw:`, i), e();
553
746
  }) : e();
554
747
  }
748
+ scopeActivatePlugin(t) {
749
+ t.element && (t.element.style.display = ""), t.onEnable?.(this);
750
+ }
555
751
  rebuildStylesheet() {
556
- const t = [];
752
+ const t = [":where(.supermouse-scope .supermouse-scope) { cursor: auto }"];
557
753
  for (const e of this._scopes) t.push(...e.buildRules());
558
- V(t);
754
+ T(this.styleOwner, t);
559
755
  }
560
756
  runPluginSafe(t, e) {
561
757
  if (t.isEnabled !== !1)
562
758
  try {
563
759
  t.update?.(this, e);
564
- } catch {
565
- t.isEnabled = !1, this.crashedPlugins.push(t);
760
+ } catch (s) {
761
+ console.error(`[Supermouse] Plugin '${t.name}' crashed and has been disabled.`, s), t.isEnabled = !1, this.crashedPlugins.push(t);
566
762
  }
567
763
  }
568
764
  cleanupCrashedPlugins() {
@@ -577,7 +773,8 @@ class Q {
577
773
  }
578
774
  try {
579
775
  t.onDisable?.(this), t.destroy?.(this);
580
- } catch {
776
+ } catch (e) {
777
+ console.error(`[Supermouse] Failed to cleanup crashed plugin '${t.name}'.`, e);
581
778
  }
582
779
  t.element?.remove();
583
780
  }
@@ -596,19 +793,19 @@ class Q {
596
793
  update(t) {
597
794
  const e = t - this.lastTime, s = Math.min(e / 1e3, 0.1);
598
795
  this.lastTime = t;
599
- const n = this.input.getCurrentTarget();
600
- n && !n.isConnected ? this.input.clearHover() : n && this.input.parseDOMInteraction(n);
601
- const r = this._activeScope;
602
- if (r) {
603
- r.stage.setVisibility(this.resolveStageVisibility()), this.input.isEnabled && r.stage.setNativeCursor(this.resolveCursorState());
604
- for (let o = 0; o < r.plugins.length; o++)
605
- this.runPluginSafe(r.plugins[o], e);
796
+ const i = this.input.getCurrentTarget();
797
+ i && !i.isConnected ? this.input.clearHover() : i && this.input.parseDOMInteraction(i), this.input.isEnabled && this.state.hasReceivedInput && (this.state.target.x = this.state.pointer.x, this.state.target.y = this.state.pointer.y);
798
+ const h = this._activeScope;
799
+ if (h) {
800
+ h.stage.setVisibility(this.resolveStageVisibility()), this.input.isEnabled && h.stage.setNativeCursor(this.resolveCursorState());
801
+ for (let r = 0; r < h.plugins.length; r++)
802
+ this.runPluginSafe(h.plugins[r], e);
606
803
  }
607
- if (this.cleanupCrashedPlugins(), this.input.isEnabled && this.state.hasReceivedInput && (this.state.target.x = this.state.pointer.x, this.state.target.y = this.state.pointer.y), this.input.isEnabled) {
608
- const o = this.state.reducedMotion ? 1e3 : 1 / this.options.smoothness * 2, d = this.state.smooth.x, c = this.state.smooth.y;
609
- this.state.smooth.x = P(this.state.smooth.x, this.state.target.x, o, s), this.state.smooth.y = P(this.state.smooth.y, this.state.target.y, o, s), this.state.displacement.x = this.state.target.x - this.state.smooth.x, this.state.displacement.y = this.state.target.y - this.state.smooth.y, s > 0 ? (this.state.velocity.x = (this.state.smooth.x - d) / s, this.state.velocity.y = (this.state.smooth.y - c) / s) : (this.state.velocity.x = 0, this.state.velocity.y = 0);
610
- const { x: a, y: h } = this.state.velocity;
611
- (Math.abs(a) > 0.1 || Math.abs(h) > 0.1) && (this.state.angle = Math.atan2(h, a) * (180 / Math.PI));
804
+ if (this.cleanupCrashedPlugins(), this.input.isEnabled) {
805
+ const r = this.state.reducedMotion ? 1e3 : 1 / this.options.smoothness * 2, o = this.state.smooth.x, p = this.state.smooth.y;
806
+ this.state.smooth.x = m(this.state.smooth.x, this.state.target.x, r, s), this.state.smooth.y = m(this.state.smooth.y, this.state.target.y, r, s), this.state.displacement.x = this.state.target.x - this.state.smooth.x, this.state.displacement.y = this.state.target.y - this.state.smooth.y, s > 0 ? (this.state.velocity.x = (this.state.smooth.x - o) / s, this.state.velocity.y = (this.state.smooth.y - p) / s) : (this.state.velocity.x = 0, this.state.velocity.y = 0);
807
+ const { x: c, y: a } = this.state.velocity;
808
+ (Math.abs(c) > 0.1 || Math.abs(a) > 0.1) && (this.state.angle = Math.atan2(a, c) * (180 / Math.PI));
612
809
  }
613
810
  }
614
811
  tick = (t) => {
@@ -627,37 +824,22 @@ class Q {
627
824
  this._running || (this._running = !0, !document.hidden && (this.lastTime = performance.now(), this.rafId = requestAnimationFrame(this.tick)));
628
825
  }
629
826
  /**
630
- * @deprecated Prefer setting `hoverSelectors` at scope creation, or use
631
- * `definePlugin`'s `selector` option. This method is kept for raw-object
632
- * plugins and runtime extension; it mutates the active scope's selector set.
827
+ * Adds one or more hover selectors to the current scope. Intended for
828
+ * raw-object plugins during `install`. Plugins written with `definePlugin`
829
+ * should use the `selector` option instead, and consumers setting up a
830
+ * scope should prefer the `hoverSelectors` option at construction.
633
831
  */
634
832
  registerHoverTarget(t) {
635
833
  const e = this._installingScope ?? this._activeScope ?? this._scopes[0];
636
834
  if (e)
637
835
  for (const s of t.split(",")) {
638
- const n = s.trim();
639
- n && e.hoverSelectors.add(n);
836
+ const i = s.trim();
837
+ i && e.hoverSelectors.add(i);
640
838
  }
641
839
  }
642
- /**
643
- * @deprecated Read from the active scope instead. Exposed for tests and
644
- * debugging only.
645
- */
646
- get hoverSelectors() {
647
- const t = this._activeScope ?? this._scopes[0];
648
- return t ? t.hoverSelectors : /* @__PURE__ */ new Set();
649
- }
650
- /**
651
- * @deprecated Read from the active scope instead. Exposed for tests and
652
- * debugging only.
653
- */
654
- get plugins() {
655
- const t = this._activeScope ?? this._scopes[0];
656
- return t ? t.plugins : [];
657
- }
658
840
  }
659
841
  export {
660
- w as DEFAULT_CURSOR_POLICY,
661
- D as DEFAULT_HOVER_SELECTORS,
662
- Q as Supermouse
842
+ S as DEFAULT_CURSOR_POLICY,
843
+ _ as DEFAULT_HOVER_SELECTORS,
844
+ I as Supermouse
663
845
  };