@supermousejs/core 2.2.0 → 2.4.0

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,49 @@
1
1
  # @supermousejs/core
2
2
 
3
+ ## 2.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 53b7276: This release stabilises the core after experimental work, keeping the best improvements while reverting the approach that caused cursor flickering.
8
+ - `zIndex` option – customise the stage’s z‑index (default `9999`).
9
+ - `cacheCursorStyle` option – opt‑in per‑element caching of computed `cursor` values, with `clearStyleCache()` to invalidate.
10
+ - `suspend()` / `resume()` – temporarily yield to another Supermouse instance without tearing down the current one.
11
+ - `hasSeenPointer` flag – enables `enable()` to snap the cursor to the live pointer instantly, no more off‑screen sweep.
12
+ - Nested‑instance CSS exclusion – scoped stylesheets now automatically prevent outer instance cursor rules from leaking into nested containers.
13
+ - `enable()` now respects any active `forcedCursor` override.
14
+ - `disable()` always restores the native cursor.
15
+ - `clearHover()` now resets `isNative` and `nativeTarget` to prevent stale state.
16
+ - Plugin crash handling: crashing plugins are removed after the frame with full cleanup (`onDisable` → `destroy` → element removal).
17
+ - `enablePlugin` / `disablePlugin` now hide/show the plugin’s DOM element (if it exposes an `element` property) and call lifecycle hooks.
18
+ - `reset()` no longer wipes `state.pointer`, so `enable()` can always snap to the last known position.
19
+ - Native‑cursor suppression now uses a class‑toggled CSS approach with additional rules for `<label>`, `<select>`, and range slider thumbs.
20
+ - `handleMouseOut` clears `isNative` only when truly leaving the native element, preventing flicker when moving within native controls.
21
+ - Animation loop pauses when the tab is hidden and resumes on focus.
22
+ - `Stage` warns if the container is not attached to the DOM yet.
23
+ - Original container `cursor` style is restored on destroy.
24
+ - Various other stability and edge‑case fixes.
25
+
26
+ - 0cd6a04: - Unified cursor modes to reduce api surface by replacing cursor and hideCursor with single cursor option, decouple interaction parsing causing bugs
27
+ - Cache rule entries and hover selector string to reduce per-frame work.
28
+ - Suspend/resume no longer toggle native cursor state. this is an identified multi-scope limitation.
29
+ - ab3cad2: Enhanced supermouse containerization and added options for caching computed cursor styles
30
+
31
+ ### Patch Changes
32
+
33
+ - 36d5366: Added proper description messages to package meta
34
+ - 4fc5aed: Added forward-compatible `onBeforeDisable` hook for plugins in preparation for architecture rewrite in v2.5
35
+
36
+ ## 2.3.0
37
+
38
+ ### Minor Changes
39
+
40
+ - 8dc8e06: cleanup and robustness pass: better case-insensitive data-\* handling, safer plugin lifecycle behavior, more reliable cursor restoration, and a more consistent reset/update flow
41
+ - 600de13: added `dataPrefix` option for customizable data attribute handling, added a `start()` public method to manually start the raf loop and enhanced supermouse's resistance to bugs
42
+
43
+ ### Patch Changes
44
+
45
+ - b72e264: Renamed `setCursor` to `setNativeCursor` for simplicity and removed redundant checks on `ignoreOnNative`
46
+
3
47
  ## 2.2.0
4
48
 
5
49
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
+ /** Default selectors that trigger `state.isHover`. Override with `hoverSelectors`. */
1
2
  export declare const DEFAULT_HOVER_SELECTORS: string[];
2
3
 
3
4
  /* Excluded from this release type: Input */
4
5
 
5
6
  /**
6
- * The Interface for interaction state.
7
- * Plugins should use Module Augmentation to add their specific properties to this interface.
7
+ * Interaction state consumed by plugins.
8
+ * Extend via module augmentation for type safety.
8
9
  *
9
10
  * @example
10
11
  * declare module '@supermousejs/core' {
@@ -15,10 +16,7 @@ export declare const DEFAULT_HOVER_SELECTORS: string[];
15
16
  * }
16
17
  */
17
18
  export declare interface InteractionState {
18
- /**
19
- * Allow arbitrary keys for rapid prototyping.
20
- * For type safety, use module augmentation to define expected keys.
21
- */
19
+ /** Arbitrary keys allowed for quick prototyping. */
22
20
  [key: string]: any;
23
21
  }
24
22
 
@@ -28,42 +26,49 @@ export declare interface MousePosition {
28
26
  }
29
27
 
30
28
  export declare interface MouseState {
31
- /** The raw position of the input pointer (mouse/touch). */
29
+ /** Raw pointer position from the latest event (before smoothing). */
32
30
  pointer: MousePosition;
33
- /** The target position the cursor logic wants to reach. */
31
+ /** Goal position the core loop drives toward. */
34
32
  target: MousePosition;
35
- /** The smoothed/interpolated position used for rendering. */
33
+ /** Smoothed position used for rendering. */
36
34
  smooth: MousePosition;
37
- /** The current velocity vector of the smooth position. */
35
+ /** Movement vector derived from smoothed state. */
38
36
  velocity: MousePosition;
39
- /** The angle of movement in degrees. Calculated from velocity. */
37
+ /** Remaining distance to target. */
38
+ displacement: MousePosition;
39
+ /** Movement angle in degrees. */
40
40
  angle: number;
41
- /** Whether the pointer is currently pressed down. */
41
+ /** Pointer is pressed down. */
42
42
  isDown: boolean;
43
- /** Whether the pointer is currently hovering over a registered interactive element. */
43
+ /** Hovering over a registered interactive element. */
44
44
  isHover: boolean;
45
- /** Whether the native cursor is currently forced visible by internal logic (e.g. input elements). */
45
+ /** Native cursor temporarily restored due to native-input heuristics. */
46
46
  isNative: boolean;
47
- /**
48
- * If set, this overrides all auto-detection logic.
49
- * 'auto' = Force Native Cursor (Show)
50
- * 'none' = Force Custom Cursor (Hide Native)
51
- * null = Let the Core decide based on isNative/isHover
52
- */
53
- forcedCursor: "auto" | "none" | null;
54
- /** The DOM element currently being hovered, if any. */
47
+ /** Current cursor mode: auto, custom, native, or both. */
48
+ cursorMode: "auto" | "custom" | "native" | "both";
49
+ /** Currently hovered DOM element, if any. */
55
50
  hoverTarget: HTMLElement | null;
56
- /** Whether the user has `prefers-reduced-motion` enabled. */
51
+ /** User has `prefers-reduced-motion` enabled. */
57
52
  reducedMotion: boolean;
58
- /** Whether the system has received valid input coordinates at least once. */
53
+ /** At least one valid input coordinate received. */
59
54
  hasReceivedInput: boolean;
60
- /** Defines a specific geometric shape the cursor should conform to. */
55
+ /** Geometric shape the cursor should conform to. */
61
56
  shape: ShapeState | null;
62
- /** Centralized store for hover metadata from data attributes. */
57
+ /** Centralized store for hover metadata from data attributes and rules. */
63
58
  interaction: InteractionState;
64
59
  }
65
60
 
66
- export declare type NativeIgnoreStrategy = "auto" | "tag" | "css";
61
+ /**
62
+ * The subset of `SupermouseOptions` guaranteed to have a concrete value once
63
+ * the constructor has merged user input over the defaults.
64
+ */
65
+ declare type ResolvedOptions = SupermouseOptions & Required<Pick<SupermouseOptions, "smoothness" | "enableTouch" | "autoDisableOnMobile" | "cursor" | "hideOnLeave" | "autoStart" | "container" | "dataPrefix" | "zIndex">>;
66
+
67
+ export declare type RuleDefinition = RuleSet | ((el: HTMLElement) => RuleSet);
68
+
69
+ export declare type RuleSet = Record<string, RuleValue | ((el: HTMLElement) => RuleValue)>;
70
+
71
+ export declare type RuleValue = string | boolean | number;
67
72
 
68
73
  export declare interface ShapeState {
69
74
  width: number;
@@ -74,100 +79,78 @@ export declare interface ShapeState {
74
79
  /* Excluded from this release type: Stage */
75
80
 
76
81
  /**
77
- * Supermouse Runtime Loop
78
- *
79
- * This class orchestrates the application state, manages the animation loop,
80
- * and coordinates data flow between the internal systems, and the plugins.
81
- *
82
- * @default
82
+ * Orchestrates state, animation loop, and plugin lifecycle.
83
83
  */
84
84
  export declare class Supermouse {
85
85
  static readonly version: string;
86
86
  readonly version: string;
87
87
  state: MouseState;
88
- /**
89
- * Configuration options.
90
- */
91
- options: SupermouseOptions;
88
+ /** Configuration options, fully resolved with defaults applied. */
89
+ options: ResolvedOptions;
92
90
  private plugins;
93
- private stage;
91
+ private _stage;
94
92
  private input;
95
93
  private rafId;
96
94
  private lastTime;
97
95
  private isRunning;
96
+ private isSuspended;
97
+ private visibilityAbortController;
98
98
  private hoverSelectors;
99
- /**
100
- * Creates a new Supermouse instance.
101
- *
102
- * @param options - Global configuration options.
103
- * @throws Will throw if running in a non-browser environment (window/document undefined).
104
- */
99
+ private hoverSelectorString;
100
+ private crashedPlugins;
105
101
  constructor(options?: SupermouseOptions);
106
- /**
107
- * Retrieves a registered plugin instance by its unique name.
108
- */
102
+ /** Look up a registered plugin by name. */
109
103
  getPlugin(name: string): SupermousePlugin | undefined;
110
- /**
111
- * Returns whether the cursor system is currently enabled (processing input).
112
- */
104
+ /** Whether the instance is not disabled/suspended and is processing input. */
113
105
  get isEnabled(): boolean;
114
- /**
115
- * Enables a specific plugin by name.
116
- * Triggers the `onEnable` lifecycle hook of the plugin.
117
- */
106
+ /** Enable a plugin by name. */
118
107
  enablePlugin(name: string): void;
119
- /**
120
- * Disables a specific plugin by name.
121
- * Triggers the `onDisable` lifecycle hook.
122
- */
108
+ /** Disable a plugin by name and hide its element. */
123
109
  disablePlugin(name: string): void;
124
- /**
125
- * Toggles the enabled state of a plugin.
126
- */
110
+ /** Toggle a plugin's enabled state by name. */
127
111
  togglePlugin(name: string): void;
112
+ /** Add a selector to hover detection and cursor suppression. */
128
113
  registerHoverTarget(selector: string): void;
129
- /**
130
- * The fixed container element where plugins should append their DOM nodes.
131
- */
132
- get container(): HTMLDivElement;
133
- /**
134
- * Manually override the native cursor visibility.
135
- *
136
- * @param type 'auto' (Show Native), 'none' (Hide Native), or null (Resume Auto-detection)
137
- */
138
- setCursor(type: "auto" | "none" | null): void;
114
+ /** The DOM element the instance is scoped to. */
115
+ get container(): HTMLElement;
116
+ /** The stage element that plugins append their visuals into. */
117
+ get stage(): HTMLDivElement;
118
+ /** Set the current cursor mode. */
119
+ setCursor(mode: "auto" | "custom" | "native" | "both"): void;
139
120
  private init;
121
+ /** Re‑enable input processing and re‑apply cursor state. */
140
122
  enable(): void;
123
+ /** Disable input processing and restore native cursor. */
141
124
  disable(): void;
142
- /**
143
- * Registers a new plugin.
144
- *
145
- * @param plugin - The plugin object to install.
146
- */
125
+ /** Temporarily yield to a scoped instance. */
126
+ suspend(): void;
127
+ /** Resume from `suspend()`. */
128
+ resume(): void;
129
+ /** Register a new plugin. */
147
130
  use(plugin: SupermousePlugin): this;
148
- private resetCoords;
149
- private resetPosition;
131
+ /** Reset physics; optionally clear all input state. */
132
+ private reset;
150
133
  private startLoop;
151
- /**
152
- * Manually steps the animation loop.
153
- *
154
- * @param time Current timestamp in milliseconds.
155
- */
134
+ /** Start the animation loop. */
135
+ start(): void;
136
+ /** Manually step the animation loop. */
156
137
  step(time: number): void;
157
138
  private runPluginSafe;
158
- /**
159
- * Runs on every animation frame.
160
- */
139
+ private cleanupCrashedPlugins;
140
+ private resolveStageVisibility;
141
+ private resolveCursorState;
142
+ private resetMotion;
143
+ private update;
161
144
  private tick;
162
- /**
163
- * Destroys the instance.
164
- */
145
+ /** Pause rAF loop when tab hidden; resume on visible. */
146
+ private bindVisibilityHandling;
147
+ /** Destroy the instance, freeing all resources. */
165
148
  destroy(): void;
166
149
  }
167
150
 
168
- /**
169
- * Configuration options passed to the Supermouse constructor.
170
- */
151
+ export declare type SupermouseInstance = Supermouse;
152
+
153
+ /** Configuration options for the Supermouse constructor. */
171
154
  export declare interface SupermouseOptions {
172
155
  /**
173
156
  * The interpolation factor (0 to 1). Lower is smoother/slower.
@@ -190,21 +173,16 @@ export declare interface SupermouseOptions {
190
173
  */
191
174
  autoDisableOnMobile?: boolean;
192
175
  /**
193
- * Strategy for detecting when to fallback to the native cursor.
194
- * - `true` / `'auto'`: Checks both HTML tags and CSS cursor styles (Accurate but slower).
195
- * - `'tag'`: Checks only semantic tags like <input>, <textarea> (Fastest, prevents layout thrashing).
196
- * - `'css'`: Checks only computed CSS cursor styles (Slow, triggers reflow).
197
- * - `false`: Never fallback to native cursor.
198
- * @default 'auto'
199
- */
200
- ignoreOnNative?: boolean | NativeIgnoreStrategy;
201
- /**
202
- * Whether to hide the native cursor via global CSS injection.
203
- * @default true
176
+ * Overall cursor mode.
177
+ * - `"auto"`: use built-in heuristic to decide per element.
178
+ * - `"custom"`: always show custom cursor, hide native.
179
+ * - `"native"`: always show native cursor, hide custom.
180
+ * - `"both"`: always show custom cursor **and** native cursor together (no suppression).
181
+ * @default "auto"
204
182
  */
205
- hideCursor?: boolean;
183
+ cursor?: "auto" | "custom" | "native" | "both";
206
184
  /**
207
- * Whether to hide the custom cursor when the mouse leaves the browser window.
185
+ * Whether to hide the custom cursor when the pointer leaves the browser viewport.
208
186
  * @default true
209
187
  */
210
188
  hideOnLeave?: boolean;
@@ -224,41 +202,49 @@ export declare interface SupermouseOptions {
224
202
  autoStart?: boolean;
225
203
  /**
226
204
  * Semantic rules mapping CSS selectors to interaction state.
205
+ * Rules are evaluated against hovered elements and merged into `state.interaction`.
227
206
  * @example { 'button': { icon: 'pointer' } }
228
207
  */
229
- rules?: Record<string, InteractionState>;
208
+ rules?: Record<string, RuleDefinition>;
230
209
  /**
231
- * Custom strategy to resolve interaction state from a hovered element.
232
- * Overrides the default data-attribute scraping.
210
+ * The prefix used for data attributes to store hover metadata.
211
+ * @default "supermouse"
233
212
  */
234
- resolveInteraction?: (target: HTMLElement) => InteractionState;
213
+ dataPrefix?: string;
214
+ /**
215
+ * The `z-index` applied to the cursor stage element.
216
+ * @default 9999
217
+ */
218
+ zIndex?: number;
235
219
  }
236
220
 
237
- /**
238
- * Interface for defining a Supermouse Plugin.
239
- */
221
+ /** Interface for defining a Supermouse plugin. */
240
222
  export declare interface SupermousePlugin {
241
- /** Unique name for the plugin. Used for toggling/retrieval. */
223
+ /** Unique plugin name. */
242
224
  name: string;
243
- /** Execution priority. Lower numbers run first. */
225
+ /** Execution priority; lower runs first. */
244
226
  priority?: number;
245
- /** If false, update() will not be called. */
227
+ /** If false, `update()` will not be called. */
246
228
  isEnabled?: boolean;
229
+ /** Root DOM element, if any. Hidden when plugin disabled. */
230
+ element?: HTMLElement | SVGElement;
247
231
  /** Called when `app.use()` is executed. */
248
- install?: (instance: Supermouse) => void;
249
- /** Called on every animation frame. */
250
- update?: (instance: Supermouse, deltaTime: number) => void;
251
- /** Called when the plugin is removed or the app is destroyed. */
252
- destroy?: (instance: Supermouse) => void;
253
- /** Called when the plugin is enabled via .enablePlugin() */
254
- onEnable?: (instance: Supermouse) => void;
255
- /** Called when the plugin is disabled via .disablePlugin() */
256
- onDisable?: (instance: Supermouse) => void;
232
+ install?: (instance: SupermouseInstance) => void;
233
+ /** Called every animation frame with delta time in ms. */
234
+ update?: (instance: SupermouseInstance, deltaTime: number) => void;
235
+ /** Called when plugin removed or instance destroyed. */
236
+ destroy?: (instance: SupermouseInstance) => void;
237
+ /** Called when plugin enabled via `.enablePlugin()`. */
238
+ onEnable?: (instance: SupermouseInstance) => void;
239
+ /** Called when plugin disabled via `.disablePlugin()`. */
240
+ onDisable?: (instance: SupermouseInstance) => void;
241
+ /**
242
+ * Called before the plugin is disabled.
243
+ */
244
+ onBeforeDisable?(instance: SupermouseInstance): void | Promise<void>;
257
245
  }
258
246
 
259
- /**
260
- * Allows a property to be a static value or a function that returns the value based on state.
261
- */
247
+ /** Static value or function that returns a value based on state. */
262
248
  export declare type ValueOrGetter<T> = T | ((state: MouseState) => T);
263
249
 
264
250
  export { }