@supermousejs/core 2.5.0-beta.2 → 2.5.0-beta.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,59 @@
1
1
  # @supermousejs/core
2
2
 
3
+ ## 2.5.0-beta.4
4
+
5
+ ### Patch Changes
6
+
7
+ - cb9bf9a: Beta.4
8
+
9
+ **Added**
10
+ - `state.authoredCursor` — the cursor value the page intends at the element under the pointer, resolved as if Supermouse's suppression were not active. Populated on every pointerTarget change, in all cursor modes. Read this to know what the page wants (`canvas { cursor: crosshair }`, `.drag-handle { cursor: grab }`, `[disabled] { cursor: not-allowed }`) rather than just whether the OS cursor should be shown (`state.isNative`).
11
+ - `state.pointerTarget` — the raw element under the pointer, regardless of hover selectors. Distinct from `state.hoverTarget`, which reports the nearest ancestor matching a hover selector.
12
+
13
+ **Fixed**
14
+ - Cursor detection in `auto` mode no longer reads its own suppression output. The previous implementation called `getComputedStyle(target).cursor` while the scope's hide class was active, so it always saw `"none"` and never triggered native fallback for authored exotic cursors. A probe attribute (`data-sm-probe`) is now excluded from every generated suppression rule, so the read sees the page's intended value.
15
+
16
+ **Changed**
17
+ - `@supermousejs/labs`: `SmartIcon` now reads `state.pointerTarget` and `state.authoredCursor` instead of relying on a broad hover-selector sweep. Hovering a `<p>` no longer sets `state.isHover` on the primary scope.
18
+ - `@supermousejs/labs`: `SmartIconOptions.useSemanticTags` renamed to `useSemanticDetection`. The old name referred to a `registerHoverTarget` sweep that no longer runs.
19
+
20
+ **Internal**
21
+ - Size budget raised to 6.5 kB gzip / 5.75 kB brotli to accommodate the beta.4 surface. Will be re-tightened once the shape settles.
22
+
23
+ ## 2.5.0-beta.3
24
+
25
+ ### Patch Changes
26
+
27
+ - 264bcbd: Beta.3 fixes and internal refactors.
28
+
29
+ **Fixed**
30
+ - Logic plugins (`priority < 0`) now correctly override `state.target` before physics. The update loop was reordering `target = pointer` after plugin execution, silently clobbering Magnetic, Stick, and any plugin that pins the cursor.
31
+ - Cursor state now applies synchronously on `mouseover`, `setCursor()`, and `handle.deactivate()`. Previously the cursor and stage visibility only updated on the next animation frame, causing a one-frame flash of the native cursor when entering a scope whose cursor mode differs from the previous one.
32
+ - Nested scope containers no longer inherit `cursor: none` from an outer scope. An engine-level `:where(.supermouse-scope .supermouse-scope) { cursor: auto }` rule cuts the inheritance without beating any authored cursor rule. Verified in Chromium and Firefox.
33
+ - `disablePlugin` on a plugin whose scope is inactive no longer re-fires `onBeforeDisable` and `onDisable`. Those hooks already ran when the scope went inactive. Semantics: `isEnabled` is user intent; lifecycle hooks fire on transitions of `(isEnabled && scopeIsActive)`.
34
+ - Deactivated eager scopes no longer orphan. The previous resolution model deleted the map entry on deactivation and never re-added it, so `handle.activate()` followed by a hover couldn't reactivate.
35
+ - Detached scope containers no longer pin stale bindings.
36
+ - `handle.activate()` is now eager when the pointer is already inside the container — fixes modals that mount under a stationary pointer.
37
+
38
+ **Changed**
39
+ - `ScopeHandle.remove()` renamed to `ScopeHandle.destroy()`.
40
+ - `ScopeHandle.disable()` / `ScopeHandle.enable()` renamed to `ScopeHandle.deactivate()` / `ScopeHandle.activate()`.
41
+ - `ScopeHandle.disabled` renamed to `ScopeHandle.active` (inverted).
42
+ - `CursorPolicy` is now `{ native: string[], hide: string[] }`. The previous `{ rules: CursorTargetRule[] }` shape is removed; `CursorTargetRule` is no longer exported.
43
+ - `ScopeConfig.container` accepts `HTMLElement | string`. A string is a CSS selector for lazy resolution. The scope stays dormant until an element matching the selector appears and the pointer enters it.
44
+ - `Stage.setNativeCursor` no longer writes inline `cursor` on the container. Cursor suppression is class-driven.
45
+
46
+ **Removed**
47
+ - `ViewportManager` module. Container rect reads are now direct `getBoundingClientRect()` calls in `Input.applyPointerToState`. Fixes stale-rect behavior after enter animations.
48
+ - Internal `scopeByContainer` map. Replaced with an ancestor walk using `Scope.match(el)`.
49
+
50
+ **Internal**
51
+ - Build target raised to `es2022`.
52
+ - `sideEffects: false` added to package.json.
53
+ - Size budget enforced at 6 kB gzip / 5.5 kB brotli via `size-limit`.
54
+
55
+ - e9431a5: Renamed `disable` and `enable` to `deactivate` and `activate` for clarify of function and implement lazy loading of containers
56
+
3
57
  ## 2.5.0-beta.2
4
58
 
5
59
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -1,39 +1,32 @@
1
1
  /** Overall cursor mode. */
2
2
  export declare type CursorMode = "auto" | "custom" | "native" | "both";
3
3
 
4
+ /**
5
+ * A cursor policy maps two independent concerns to flat selector lists.
6
+ *
7
+ * - `native`: selectors whose elements yield to the OS cursor when the
8
+ * cursor mode is `"auto"`. Hovering them temporarily shows the native
9
+ * cursor and hides the custom one.
10
+ *
11
+ * - `hide`: selectors that receive `cursor: none !important` when the
12
+ * custom cursor is active. Needed for elements whose UA stylesheet sets
13
+ * a cursor (`a { cursor: pointer }`) that would otherwise override the
14
+ * inherited suppression.
15
+ */
4
16
  export declare interface CursorPolicy {
5
- rules: CursorTargetRule[];
17
+ native: string[];
18
+ hide: string[];
6
19
  }
7
20
 
8
- /** Shorthand form accepted at the public API boundary. */
21
+ /**
22
+ * Shorthand accepted at the public API boundary. `{ native?: [...], hide?: [...] }`
23
+ * normalizes to the full `CursorPolicy` shape with missing keys defaulting to `[]`.
24
+ */
9
25
  export declare type CursorPolicyInput = CursorPolicy | {
10
26
  native?: string[];
11
27
  hide?: string[];
12
28
  };
13
29
 
14
- /**
15
- * A single rule that ties a CSS selector to native-cursor behaviour
16
- * and/or native-cursor CSS suppression.
17
- */
18
- export declare interface CursorTargetRule {
19
- /** CSS selector to match. */
20
- selector: string;
21
- /**
22
- * In `"auto"` cursor mode, an element matching this selector yields to the
23
- * OS cursor.
24
- * @default true
25
- */
26
- native?: boolean;
27
- /**
28
- * Generate a `cursor: none !important` rule for this selector when the
29
- * custom cursor is active. Needed for elements whose UA stylesheet sets a
30
- * cursor value (e.g. `a { cursor: pointer }`) that would otherwise override
31
- * the container's inherited `cursor: none`.
32
- * @default true
33
- */
34
- hide?: boolean;
35
- }
36
-
37
30
  export declare const DEFAULT_CURSOR_POLICY: CursorPolicy;
38
31
 
39
32
  export declare const DEFAULT_HOVER_SELECTORS: string[];
@@ -81,6 +74,16 @@ export declare interface MouseState {
81
74
  isNative: boolean;
82
75
  /** Current cursor mode: auto, custom, native, or both. */
83
76
  cursorMode: CursorMode;
77
+ /**
78
+ * The raw element currently under the pointer, regardless of whether it
79
+ * matches any hover selector.
80
+ */
81
+ pointerTarget: HTMLElement | null;
82
+ /**
83
+ * The authored cursor value at `pointerTarget`, resolved as if
84
+ * Supermouse's suppression were not active.
85
+ */
86
+ authoredCursor: string | null;
84
87
  /** Currently hovered DOM element, if any. */
85
88
  hoverTarget: HTMLElement | null;
86
89
  /** User has `prefers-reduced-motion` enabled. */
@@ -114,8 +117,11 @@ export declare type RuleValue = string | boolean | number;
114
117
  export declare interface ScopeConfig {
115
118
  /** Optional identifier for `getScope(name)` lookups. */
116
119
  name?: string;
117
- /** The element this scope is bound to. */
118
- container: HTMLElement;
120
+ /**
121
+ * The container this scope is bound to. Pass an element reference for
122
+ * eager binding, or a CSS selector string for lazy resolution.
123
+ */
124
+ container: HTMLElement | string;
119
125
  /** Cursor mode for this scope. Inherits from top-level if omitted. */
120
126
  cursor?: CursorMode;
121
127
  /** Hover selectors for this scope. Inherits from top-level if omitted. */
@@ -142,12 +148,31 @@ export declare interface ScopeConfig {
142
148
 
143
149
  export declare interface ScopeHandle {
144
150
  readonly name: string | undefined;
145
- readonly container: HTMLElement;
146
- readonly disabled: boolean;
147
- remove(): void;
151
+ /**
152
+ * The scope's container element, or `null` if the scope is
153
+ * selector-based and has not yet been resolved to a live element.
154
+ */
155
+ readonly container: HTMLElement | null;
156
+ readonly active: boolean;
157
+ /** True for eager scopes and for selector scopes that have resolved. */
158
+ readonly resolved: boolean;
159
+ destroy(): void;
148
160
  setCursor(mode: CursorMode): void;
149
- disable(): void;
150
- enable(): void;
161
+ /**
162
+ * Marks the scope as eligible for activation.
163
+ *
164
+ * Activation is lazy: the scope becomes active on the next `mouseover`
165
+ * inside its container, not immediately. If you need to force the scope
166
+ * active while the pointer is already inside it, this will not work
167
+ * until the pointer moves. Deactivation, by contrast, is eager.
168
+ */
169
+ activate(): void;
170
+ /**
171
+ * Marks the scope as ineligible. If it was the active scope, control
172
+ * yields immediately to the nearest active ancestor scope, or to
173
+ * nothing if none exists.
174
+ */
175
+ deactivate(): void;
151
176
  use(plugin: SupermousePlugin): ScopeHandle;
152
177
  removePlugin(name: string): void;
153
178
  getPlugin(name: string): SupermousePlugin | undefined;
@@ -167,7 +192,6 @@ export declare class Supermouse {
167
192
  private _scopes;
168
193
  private _activeScope;
169
194
  private _installingScope;
170
- private scopeByContainer;
171
195
  private scopeByName;
172
196
  private scopeHandles;
173
197
  private input;
@@ -181,12 +205,42 @@ export declare class Supermouse {
181
205
  get stage(): HTMLDivElement;
182
206
  get isEnabled(): boolean;
183
207
  get isRunning(): boolean;
208
+ /**
209
+ * Installs a plugin into the primary scope.
210
+ *
211
+ * In multi-scope mode, prefer `handle.use(plugin)` to target a specific
212
+ * scope. This method always installs to the primary scope, regardless of
213
+ * which scope is currently active.
214
+ */
184
215
  use(plugin: SupermousePlugin): this;
216
+ /**
217
+ * Searches every scope and returns the first plugin matching `name`.
218
+ * Scopes are searched in registration order (primary first).
219
+ */
185
220
  getPlugin(name: string): SupermousePlugin | undefined;
186
221
  getScope(name: string): ScopeHandle | undefined;
222
+ /**
223
+ * Enables a plugin by name, wherever it lives.
224
+ *
225
+ * If the plugin's scope is currently active, the activation lifecycle
226
+ * runs immediately. If the scope is inactive, `isEnabled` is set but
227
+ * hooks do not fire until the scope next activates.
228
+ */
187
229
  enablePlugin(name: string): void;
230
+ /**
231
+ * Disables a plugin by name, wherever it lives.
232
+ *
233
+ * If the plugin's scope is currently active, the deactivation lifecycle
234
+ * runs immediately. If the scope is inactive, `isEnabled` is set but
235
+ * hooks do not fire — they already ran when the scope went inactive.
236
+ */
188
237
  disablePlugin(name: string): void;
189
238
  togglePlugin(name: string): void;
239
+ /**
240
+ * Sets the cursor mode on the *active* scope.
241
+ *
242
+ * To set the mode on a specific scope, use `handle.setCursor(mode)`.
243
+ */
190
244
  setCursor(mode: CursorMode): void;
191
245
  addScope(config: ScopeConfig): ScopeHandle;
192
246
  enable(): void;
@@ -200,11 +254,42 @@ export declare class Supermouse {
200
254
  private _running;
201
255
  private createScope;
202
256
  private buildScopeHandle;
203
- private removeScope;
257
+ private destroyScope;
204
258
  private setScopeCursor;
205
- private disableScope;
206
- private enableScope;
207
- private findScopeAbove;
259
+ /**
260
+ * Marks the scope ineligible for activation and, if it was active, yields
261
+ * immediately to the nearest active ancestor scope (or nothing).
262
+ */
263
+ private deactivateScope;
264
+ /**
265
+ * Marks the scope eligible for activation.
266
+ *
267
+ * If the pointer is already inside the scope's container, the scope
268
+ * becomes active immediately. Otherwise it activates on the next
269
+ * `mouseover` inside the container. Deactivation is always eager.
270
+ */
271
+ private activateScope;
272
+ /**
273
+ * Called by `Input` on every `mouseover`, and internally when the active
274
+ * scope is removed or deactivated. Walks from `node` up the ancestor
275
+ * chain, returning the innermost active scope whose `match` succeeds
276
+ * for an ancestor. The matched scope is bound to that ancestor before
277
+ * return.
278
+ *
279
+ * The walk order is what enforces "innermost wins": the first ancestor
280
+ * matching any active scope is, by construction, the closest one to the
281
+ * event target. Detached containers can never be ancestors of a live
282
+ * event target, so no cleanup of stale bindings is required here.
283
+ */
284
+ private resolveScopeForNode;
285
+ /**
286
+ * Synchronously writes the current cursor and stage visibility for the
287
+ * active scope. Called after hover state settles, on cursor mode changes,
288
+ * and on programmatic scope transitions. The `update()` rAF path calls
289
+ * the same writers as a backstop.
290
+ */
291
+ private applyCursorNow;
292
+ private findScopeForPlugin;
208
293
  private installPlugins;
209
294
  private installPlugin;
210
295
  private removePluginFromScope;
@@ -227,16 +312,6 @@ export declare class Supermouse {
227
312
  private tick;
228
313
  private bindVisibilityHandling;
229
314
  private startLoop;
230
- /**
231
- * @deprecated Read from the active scope instead.
232
- * Exposed for tests and debugging.
233
- */
234
- get hoverSelectors(): Set<string>;
235
- /**
236
- * @deprecated Read from the active scope instead.
237
- * Exposed for tests and debugging.
238
- */
239
- get plugins(): SupermousePlugin[];
240
315
  /**
241
316
  * Adds one or more hover selectors to the current scope. Intended for
242
317
  * raw-object plugins during `install`. Plugins written with `definePlugin`