@supermousejs/core 2.3.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,38 @@
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
+
3
36
  ## 2.3.0
4
37
 
5
38
  ### 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 from the latest pointer event, before smoothing is applied. */
29
+ /** Raw pointer position from the latest event (before smoothing). */
32
30
  pointer: MousePosition;
33
- /** The current goal position that the core loop is driving toward. */
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 movement vector derived from the smoothed state. */
35
+ /** Movement vector derived from smoothed state. */
38
36
  velocity: MousePosition;
39
- /** The current movement angle in degrees, derived 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 runtime has temporarily restored the native cursor due to native-input heuristics. */
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,104 +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
- * Sets the native cursor visibility.
135
- *
136
- * @param mode
137
- */
138
- setNativeCursor(mode: "hide" | "show" | "auto"): 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;
131
+ /** Reset physics; optionally clear all input state. */
148
132
  private reset;
149
133
  private startLoop;
150
- /**
151
- * Starts the animation loop. This is automatically called if `autoStart` is true.
152
- * Plugins can call this method to resume the loop if it has been stopped.
153
- */
134
+ /** Start the animation loop. */
154
135
  start(): void;
155
- /**
156
- * Manually steps the animation loop.
157
- *
158
- * @param time Current timestamp in milliseconds.
159
- */
136
+ /** Manually step the animation loop. */
160
137
  step(time: number): void;
161
138
  private runPluginSafe;
162
- /**
163
- * Runs on every animation frame.
164
- */
139
+ private cleanupCrashedPlugins;
140
+ private resolveStageVisibility;
141
+ private resolveCursorState;
142
+ private resetMotion;
143
+ private update;
165
144
  private tick;
166
- /**
167
- * Destroys the instance.
168
- */
145
+ /** Pause rAF loop when tab hidden; resume on visible. */
146
+ private bindVisibilityHandling;
147
+ /** Destroy the instance, freeing all resources. */
169
148
  destroy(): void;
170
149
  }
171
150
 
172
- /**
173
- * Configuration options passed to the Supermouse constructor.
174
- */
151
+ export declare type SupermouseInstance = Supermouse;
152
+
153
+ /** Configuration options for the Supermouse constructor. */
175
154
  export declare interface SupermouseOptions {
176
155
  /**
177
156
  * The interpolation factor (0 to 1). Lower is smoother/slower.
@@ -194,25 +173,16 @@ export declare interface SupermouseOptions {
194
173
  */
195
174
  autoDisableOnMobile?: boolean;
196
175
  /**
197
- * Strategy for detecting when to fallback to the native cursor.
198
- * - `'auto'`: Checks both HTML tags and CSS cursor styles (Accurate but slower).
199
- * - `'tag'`: Checks only semantic tags like <input>, <textarea> (Fastest, prevents layout thrashing).
200
- * - `'css'`: Checks only computed CSS cursor styles (Slow, triggers reflow).
201
- * - `null`: Never fallback to native cursor.
202
- *
203
- * @default 'auto'
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
- ignoreOnNative?: NativeIgnoreStrategy | null;
206
- /**
207
- * Whether to hide the native cursor via scoped CSS injection on the container.
208
- * When enabled, the stage toggles `cursor: none` on the configured container and
209
- * on registered hover targets.
210
- * @default true
211
- */
212
- hideCursor?: boolean;
183
+ cursor?: "auto" | "custom" | "native" | "both";
213
184
  /**
214
185
  * Whether to hide the custom cursor when the pointer leaves the browser viewport.
215
- * When enabled the runtime clears the cursor back to an off-screen position to avoid stale hover state.
216
186
  * @default true
217
187
  */
218
188
  hideOnLeave?: boolean;
@@ -233,51 +203,48 @@ export declare interface SupermouseOptions {
233
203
  /**
234
204
  * Semantic rules mapping CSS selectors to interaction state.
235
205
  * Rules are evaluated against hovered elements and merged into `state.interaction`.
236
- * Matching data attributes on the same element are also read and can override or enrich the final object.
237
206
  * @example { 'button': { icon: 'pointer' } }
238
207
  */
239
- rules?: Record<string, InteractionState>;
240
- /**
241
- * Custom strategy to resolve interaction state from a hovered element.
242
- * When provided, this callback bypasses the default `rules` + `data-[prefix]-*` scraping and
243
- * returns the interaction payload directly for the current hover target.
244
- */
245
- resolveInteraction?: (target: HTMLElement) => InteractionState;
208
+ rules?: Record<string, RuleDefinition>;
246
209
  /**
247
210
  * The prefix used for data attributes to store hover metadata.
248
- * For example, if dataPrefix is "supermouse", then the attribute would be "data-supermouse-*".
249
- * This allows for multiple instances of Supermouse to coexist without conflicting data attributes.
250
211
  * @default "supermouse"
251
212
  */
252
213
  dataPrefix?: string;
214
+ /**
215
+ * The `z-index` applied to the cursor stage element.
216
+ * @default 9999
217
+ */
218
+ zIndex?: number;
253
219
  }
254
220
 
255
- /**
256
- * Interface for defining a Supermouse Plugin.
257
- */
221
+ /** Interface for defining a Supermouse plugin. */
258
222
  export declare interface SupermousePlugin {
259
- /** Unique name for the plugin. Used for toggling/retrieval. */
223
+ /** Unique plugin name. */
260
224
  name: string;
261
- /** Execution priority. Lower numbers run first. */
225
+ /** Execution priority; lower runs first. */
262
226
  priority?: number;
263
- /** If false, update() will not be called. */
227
+ /** If false, `update()` will not be called. */
264
228
  isEnabled?: boolean;
229
+ /** Root DOM element, if any. Hidden when plugin disabled. */
230
+ element?: HTMLElement | SVGElement;
265
231
  /** Called when `app.use()` is executed. */
266
- install?: (instance: Supermouse) => void;
267
- /** Called on every animation frame with the frame delta time in milliseconds. */
268
- update?: (instance: Supermouse, deltaTime: number) => void;
269
- /** Called when the plugin is removed or the app is destroyed. */
270
- destroy?: (instance: Supermouse) => void;
271
- /** Called when the plugin is enabled via .enablePlugin() */
272
- onEnable?: (instance: Supermouse) => void;
273
- /** Called when the plugin is disabled via .disablePlugin() */
274
- 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>;
275
245
  }
276
246
 
277
- /**
278
- * Allows a property to be a static value or a function that returns the value based on state.
279
- * This is primarily useful for plugin option definitions that should react to the current runtime state.
280
- */
247
+ /** Static value or function that returns a value based on state. */
281
248
  export declare type ValueOrGetter<T> = T | ((state: MouseState) => T);
282
249
 
283
250
  export { }