cereb 0.8.1 → 0.9.1

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/README.md CHANGED
@@ -1,175 +1,197 @@
1
1
  # Cereb
2
2
 
3
- User input modeling and orchestration with a lightweight reactive stream library.
4
-
5
- ## Installation
3
+ **User input handling and orchestration** libray,
4
+ From low-level events (keyboard, wheel, pointer) to high-level gestures (pan, pinch)
6
5
 
7
6
  ```bash
8
- npm install cereb
7
+ npm install --save cereb
9
8
  ```
10
9
 
11
- ## Quick Start
10
+ ## Getting started
12
11
 
13
- Cereb models user input as lightweight reactive streams—from low-level DOM events to pointers, and higher-level gestures like pan or pinch.
14
- Below is a minimal example for a **single pointer** stream.
12
+ The example below moves an element by tracking pointer position:
15
13
 
16
14
  ```typescript
17
15
  import { singlePointer } from "cereb";
18
16
 
19
- /**
20
- * Provides a stream for a single pointer.
21
- * - Merges DOM events from pointer start → end/cancel into one stream and normalizes them into a single, clean interface.
22
- * - Keeps pointer handling clear and purpose-driven—no need to juggle multiple DOM event shapes.
23
- * - Lets you coordinate and control priority across multiple input streams.
24
- */
25
- singlePointer(canvas).subscribe((signal) => {
26
- const { phase, x, y } = signal.value;
27
-
28
- switch (phase) {
29
- case "move":
30
- point.style.setProperty("transform", `translateX(${x}, ${y})`);
31
- break;
32
- }
33
- });
17
+ // Create a stream from pointer events
18
+ singlePointer(canvas)
19
+ // Subscribe to monitor stream changes
20
+ .subscribe((signal) => {
21
+ // Receive signals from the stream
22
+ const { phase, x, y } = signal.value;
23
+ switch (phase){
24
+ case "move":
25
+ element.style.transform = `translate(${x}px, ${y}px)`;
26
+ break;
27
+ }
28
+ });
34
29
  ```
35
30
 
36
- ## What problems does Cereb solve?
31
+ ## High-level gestures packages
37
32
 
38
- - **Unified Input Abstraction** - Handle mouse, touch, and pen with a single `SinglePointer` interface
39
- - **Composable Pipelines** - Transform streams with operators like `filter`, `map`, `throttle`, and more
40
- - **Stream Orchestration** - Schedule and coordinate multiple streams (coming soon)
41
- - **Lightweight** - Minimal overhead for high-frequency input handling
33
+ For advanced gestures like pan or pinch, install dedicated packages that build on top of Cereb's core:
42
34
 
43
- ## Recipes
35
+ | Package | Description |
36
+ |---------|-------------|
37
+ | [@cereb/pan](https://www.npmjs.com/package/@cereb/pan) | Pan/drag gestures with velocity and direction tracking |
38
+ | [@cereb/pinch](https://www.npmjs.com/package/@cereb/pinch) | Pinch-to-zoom with distance and scale calculations |
44
39
 
45
- ### Session Filtering
40
+ ### Pinch example
46
41
 
47
- Filter events to only emit during active sessions (from pointer down to up/cancel). Events outside a session are ignored.
42
+ ```bash
43
+ npm install --save cereb @cereb/pinch
44
+ ```
48
45
 
49
46
  ```typescript
50
- import { pipe, singlePointer, singlePointerSession } from "cereb";
51
-
52
- // Only emits events from start → end/cancel
53
- const stream = pipe(
54
- singlePointer(element),
55
- singlePointerSession()
56
- );
57
-
58
- stream.subscribe((signal) => {
59
- // Guaranteed to be within an active pointer session
60
- const { phase, x, y } = signal.value;
47
+ import { pipe } from "cereb";
48
+ import { zoom } from "cereb/operators";
49
+ import { pinch } from "@cereb/pinch";
50
+
51
+ // pipe creates a pipeline where signals flow through operators
52
+ // Each operator extends the signal (signals are immutable)
53
+ pipe(
54
+ pinch(element), // Create stream: a pinch gesture
55
+ zoom({ minScale: 0.5, maxScale: 3.0 }), // Operator: Determine scale value.
56
+ ).subscribe((signal) => {
57
+ // The scale property is extended from the value.
58
+ // - pinch emits distance → zoom calculates scale
59
+ // - zoom also works with other inputs (keyboard, wheel, etc.)
60
+ element.style.transform = `scale(${signal.value.scale})`;
61
61
  });
62
62
  ```
63
63
 
64
- For custom session boundaries, use `session` directly:
64
+ ## API overview
65
65
 
66
- ```typescript
67
- import { pipe, session } from "cereb";
68
-
69
- const stream = pipe(
70
- someStream,
71
- session({
72
- start: (signal) => signal.value.phase === "start",
73
- end: (signal) => signal.value.phase === "end",
74
- })
75
- );
76
- ```
66
+ Full API docs are coming soon.
67
+ In the meantime, check the source—it's well-typed and commented:
77
68
 
78
- ### DOM Events
69
+ - [Stream Factories](https://github.com/devphilip21/cereb/tree/main/packages/cereb/src/browser)
70
+ - [Operators](https://github.com/devphilip21/cereb/tree/main/packages/cereb/src/operators)
79
71
 
80
- Cereb includes factories to convert DOM events into streams, and to build higher-level streams by merging mouse/touch/pointer events.
72
+ ## The Problems Cereb Solves
81
73
 
82
- ```typescript
83
- import { domEvent, mouseEvents } from "cereb";
74
+ ### 1. Event-Driven Code Becomes Spaghetti
84
75
 
85
- const $touchScrollContainer = domEvent(scrollContainerElement, "touchstart");
86
- const $mouseSomething = mouseEvents(somethingElement);
87
- ```
88
-
89
- You can also build a `singlePointer` stream from touch events:
76
+ Traditional event handlers create **scattered logic, side effects, and duplicated code** that's hard to maintain.
77
+ See how this plays out in a multi-input zoom implementation:
90
78
 
91
79
  ```typescript
92
- import { touchEvents, pipe } from "cereb";
93
- import { singlePointerRecognizer } from "cereb/single-pointer/touch";
94
-
95
- const pointSomething$ = pipe(
96
- touchEvents(somethingElement),
97
- singlePointerRecognizer(),
98
- );
99
-
100
- pointSomething$.subscribe((signal) => { /* .. */ });
101
- ```
102
-
103
- ### Blocking Streams
80
+ // Before: Scattered handlers, shared state, duplicated logic
81
+ let currentScale = 1;
82
+ let isCtrlPressed = false;
83
+
84
+ // Track Ctrl key
85
+ window.addEventListener('keydown', e => {
86
+ if (e.key === 'Control' || e.key === 'Meta') isCtrlPressed = true;
87
+
88
+ if (isCtrlPressed && (e.key === '+' || e.key === '-')) {
89
+ e.preventDefault();
90
+ currentScale = Math.max(0.2, Math.min(5, currentScale + (e.key === '+' ? 0.15 : -0.15)));
91
+ applyScale(currentScale); // Duplicated min/max logic
92
+ }
93
+ });
104
94
 
105
- All streams are blockable - events are silently dropped when blocked:
95
+ window.addEventListener('keyup', e => {
96
+ if (e.key === 'Control' || e.key === 'Meta') isCtrlPressed = false;
97
+ });
106
98
 
107
- ```typescript
108
- import { singlePointer } from "cereb";
99
+ // Ctrl + Wheel
100
+ canvas.addEventListener('wheel', e => {
101
+ if (!isCtrlPressed) return;
102
+ e.preventDefault();
103
+ currentScale = Math.max(0.2, Math.min(5, currentScale + (-e.deltaY * 0.005)));
104
+ applyScale(currentScale); // Duplicated min/max logic again
105
+ });
109
106
 
110
- const stream$ = singlePointer(element);
107
+ // Pinch gesture (touchstart/touchmove/touchend)
108
+ // ... 20+ lines: track two fingers, calculate distance, apply scale
109
+ // ... min/max logic duplicated yet again
111
110
 
112
- stream$.subscribe((p) => console.log(p.x, p.y));
113
- stream$.block(); // Pause event processing
114
- stream$.unblock(); // Resume event processing
111
+ // Problem: 6+ scattered handlers, shared state, logic duplicated 3 times
115
112
  ```
116
113
 
117
- ## Cereb Operators
114
+ <br>
118
115
 
119
- | Operator | Description |
120
- |----------|-------------|
121
- | `offset` | Add element-relative `offsetX`, `offsetY` to pointer signals |
116
+ Cereb's solution:
117
+ Model events as streams, and you get readable, reusable, extensible declarative pipelines.
122
118
 
123
119
  ```typescript
124
- import { pipe, singlePointer, offset } from "cereb";
120
+ // After: Clear flow, no side effects, composable
121
+ import { pipe, keyboard, keyboardHeld, wheel } from "cereb";
122
+ import { zoom, extend, when } from "cereb/operators";
123
+ import { pinch } from "@cereb/pinch";
124
+
125
+ const MIN_SCALE = 0.2, MAX_SCALE = 5;
126
+ let currentScale = 1;
127
+
128
+ // Pinch-to-zoom (touch)
129
+ pipe(
130
+ pinch(canvas),
131
+ zoom({ minScale: MIN_SCALE, maxScale: MAX_SCALE })
132
+ ).subscribe(applyZoom);
133
+
134
+ // Ctrl/Cmd + Plus/Minus (keyboard)
135
+ pipe(
136
+ keyboard(window, { key: ["+", "-"], preventDefault: true }),
137
+ when(keyboardHeld(window, { modifiers: ["meta", "ctrl"] })),
138
+ extend((signal) => ({
139
+ ratio: currentScale + (signal.value.key === "+" ? 0.15 : -0.15)
140
+ })),
141
+ zoom({ minScale: MIN_SCALE, maxScale: MAX_SCALE })
142
+ ).subscribe(applyZoom);
143
+
144
+ // Ctrl/Cmd + Wheel (mouse)
145
+ pipe(
146
+ wheel(canvas, { modifiers: ["meta", "ctrl"], preventDefault: true }),
147
+ extend((signal) => ({
148
+ ratio: currentScale + (-signal.value.deltaY * 0.005)
149
+ })),
150
+ zoom({ minScale: MIN_SCALE, maxScale: MAX_SCALE })
151
+ ).subscribe(applyZoom);
152
+
153
+ function applyZoom(signal) {
154
+ currentScale = signal.value.scale;
155
+ canvas.style.transform = `scale(${currentScale})`;
156
+ }
157
+ ```
125
158
 
126
- const stream = pipe(
127
- singlePointer(element),
128
- offset({ target: element }) // includes offsetX, offsetY relative to target element
129
- );
159
+ ### 2. Lightweight Bundle Size
130
160
 
131
- stream.subscribe((signal) => {
132
- const { x, y, offsetX, offsetY } = signal.value;
133
- });
134
- ```
161
+ Benchmark: Equivalent pan gesture implementation
135
162
 
136
- ## General Operators
163
+ | | Minified | Gzipped |
164
+ |--|----------|---------|
165
+ | cereb + @cereb/pan | 4.58 KB | **1.73 KB** |
166
+ | Hammer.js | 20.98 KB | 7.52 KB |
137
167
 
138
- Core includes common stream operators:
168
+ **~77% smaller** than Hammer.js for equivalent pan gesture functionality.
139
169
 
140
- | Operator | Description |
141
- |----------|-------------|
142
- | `filter` | Emit only values that pass a predicate |
143
- | `map` | Transform each value |
144
- | `throttle` | Limit emission rate |
145
- | `debounce` | Delay until quiet period |
146
- | `take` / `skip` | Control emission count |
147
- | `merge` | Combine multiple streams |
148
- | `share` | Multicast to multiple subscribers |
149
- | `distinctUntilChanged` | Skip consecutive duplicates |
170
+ ### 3. Performance & Resource Efficiency
171
+
172
+ **1. Event Listener Reuse**
150
173
 
151
174
  ```typescript
152
- import { pipe, filter, throttle, map } from "cereb";
153
-
154
- const stream = pipe(
155
- singlePointer(element),
156
- filter((p) => p.phase !== "cancel"),
157
- throttle(16), // ~60fps
158
- map((p) => ({ x: p.x, y: p.y }))
159
- );
175
+ // Before: Multiple addEventListener calls
176
+ window.addEventListener('keydown', handler1);
177
+ window.addEventListener('keydown', handler2);
178
+ window.addEventListener('keydown', handler3);
179
+
180
+ // After: Shared stream, single listener
181
+ // addEventListener called once
182
+ keyboard(window).subscribe(handler1);
183
+ keyboard(window).subscribe(handler2);
184
+ keyboard(window).subscribe(handler3);
160
185
  ```
161
186
 
162
- ### Combining Streams
187
+ **2. Single Responsibility Operators**
163
188
 
164
189
  ```typescript
165
- import { pipe, merge, singlePointer, domEvent } from "cereb";
166
-
167
- const keyboard$ = domEvent(window, "keydown");
168
- const pointer$ = singlePointer(element);
169
-
170
- // You can subscribe to events from multiple sources as one stream.
171
- // This example simply merges, but you can orchestrate behavior with pipelines and operators.
172
- const combined$ = merge(keyboard$, pointer$);
190
+ pipe(
191
+ pan(element), // Pan gesture recognition
192
+ offset({ target }), // Element-relative coordinates
193
+ axisLock() // Lock to horizontal/vertical
194
+ )
173
195
  ```
174
196
 
175
197
  ## License
@@ -1,4 +1,19 @@
1
1
  import { Stream } from '../../core/stream.js';
2
2
  import { DomEventSignal } from './dom-event-signal.js';
3
- export declare function domEvent(target: EventTarget, eventName: string, options?: AddEventListenerOptions): Stream<DomEventSignal<Event>>;
3
+ type AnyEventMap = Record<string, Event>;
4
+ /**
5
+ * Strongly-typed event target for custom event maps.
6
+ * Useful for non-DOM EventTargets that still have named events.
7
+ */
8
+ export type TypedEventTarget<M extends AnyEventMap> = EventTarget & {
9
+ addEventListener<K extends keyof M>(type: K, listener: (event: M[K]) => void, options?: AddEventListenerOptions): void;
10
+ removeEventListener<K extends keyof M>(type: K, listener: (event: M[K]) => void, options?: AddEventListenerOptions): void;
11
+ };
12
+ export declare function domEvent<K extends keyof WindowEventMap>(target: Window, eventName: K, options?: AddEventListenerOptions): Stream<DomEventSignal<WindowEventMap[K]>>;
13
+ export declare function domEvent<K extends keyof DocumentEventMap>(target: Document, eventName: K, options?: AddEventListenerOptions): Stream<DomEventSignal<DocumentEventMap[K]>>;
14
+ export declare function domEvent<K extends keyof HTMLElementEventMap>(target: HTMLElement, eventName: K, options?: AddEventListenerOptions): Stream<DomEventSignal<HTMLElementEventMap[K]>>;
15
+ export declare function domEvent<K extends keyof SVGElementEventMap>(target: SVGElement, eventName: K, options?: AddEventListenerOptions): Stream<DomEventSignal<SVGElementEventMap[K]>>;
16
+ export declare function domEvent<M extends AnyEventMap, K extends keyof M>(target: TypedEventTarget<M>, eventName: K, options?: AddEventListenerOptions): Stream<DomEventSignal<M[K]>>;
17
+ export declare function domEvent<E extends Event = Event>(target: EventTarget, eventName: string, options?: AddEventListenerOptions): Stream<DomEventSignal<E>>;
18
+ export {};
4
19
  //# sourceMappingURL=dom-event.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dom-event.d.ts","sourceRoot":"","sources":["../../../src/browser/dom-event/dom-event.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAElF,wBAAgB,QAAQ,CACtB,MAAM,EAAE,WAAW,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,uBAAuB,GAChC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAY/B"}
1
+ {"version":3,"file":"dom-event.d.ts","sourceRoot":"","sources":["../../../src/browser/dom-event/dom-event.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAElF,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAEzC;;;GAGG;AACH,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,WAAW,IAAI,WAAW,GAAG;IAClE,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,EAChC,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,EAC/B,OAAO,CAAC,EAAE,uBAAuB,GAChC,IAAI,CAAC;IACR,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAC,EACnC,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,EAC/B,OAAO,CAAC,EAAE,uBAAuB,GAChC,IAAI,CAAC;CACT,CAAC;AAEF,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,cAAc,EACrD,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,CAAC,EACZ,OAAO,CAAC,EAAE,uBAAuB,GAChC,MAAM,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7C,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,gBAAgB,EACvD,MAAM,EAAE,QAAQ,EAChB,SAAS,EAAE,CAAC,EACZ,OAAO,CAAC,EAAE,uBAAuB,GAChC,MAAM,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/C,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,mBAAmB,EAC1D,MAAM,EAAE,WAAW,EACnB,SAAS,EAAE,CAAC,EACZ,OAAO,CAAC,EAAE,uBAAuB,GAChC,MAAM,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAClD,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,kBAAkB,EACzD,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,CAAC,EACZ,OAAO,CAAC,EAAE,uBAAuB,GAChC,MAAM,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACjD,wBAAgB,QAAQ,CAAC,CAAC,SAAS,WAAW,EAAE,CAAC,SAAS,MAAM,CAAC,EAC/D,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC3B,SAAS,EAAE,CAAC,EACZ,OAAO,CAAC,EAAE,uBAAuB,GAChC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChC,wBAAgB,QAAQ,CAAC,CAAC,SAAS,KAAK,GAAG,KAAK,EAC9C,MAAM,EAAE,WAAW,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,uBAAuB,GAChC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC"}
@@ -1,55 +1,43 @@
1
1
  import { Signal } from '../../core/signal.js';
2
2
  import { Stream } from '../../core/stream.js';
3
3
  import { ModifierKey } from './keyboard.js';
4
- export interface HeldValue {
5
- held: boolean;
4
+ interface HeldValue {
5
+ opened: boolean;
6
6
  }
7
- export interface HeldSignal extends Signal<"held", HeldValue> {
7
+ export interface HeldSignal extends Signal<"keyboard-held", HeldValue> {
8
8
  }
9
- export declare const HELD_SIGNAL_KIND: "held";
9
+ export declare const HELD_SIGNAL_KIND: "keyboard-held";
10
10
  export interface KeyboardHeldOptions {
11
11
  /**
12
12
  * The key to track. Case-insensitive.
13
- * @example 'z', 'Enter', 'Escape'
13
+ * @example 'z', 'Space', 'Escape'
14
14
  */
15
15
  key: string;
16
16
  /**
17
17
  * Modifier keys that must also be held. Uses OR logic.
18
+ *
19
+ * ⚠️ WARNING: On macOS, OS-level modifiers (especially Meta/Command) can swallow
20
+ * keyup events. This is a known platform limitation, not a bug. For reliable
21
+ * "hold to activate" behavior, prefer using non-modifier keys (Space, Shift, etc.)
22
+ * as the primary key instead of relying on modifier combinations.
23
+ *
18
24
  * @example ['meta', 'ctrl'] - matches if metaKey OR ctrlKey is pressed
19
25
  */
20
26
  modifiers?: ModifierKey[];
21
27
  }
22
28
  /**
23
- * Creates a stream that tracks whether a specific key (and optionally modifiers) is being held down.
24
- *
25
- * Automatically shares the keyboard stream for the same EventTarget, so multiple
26
- * keyboardHeld calls on the same target reuse the same underlying event listeners.
27
- *
28
- * This function correctly handles the case where modifier keys are released before
29
- * the main key. It tracks the main key's down/up state independently and checks
30
- * modifier states from each keyboard event.
31
- *
32
- * @param target - EventTarget to listen for keyboard events (e.g., window, document, element)
33
- * @param options - Key and modifier configuration
34
- * @returns Stream that emits { held: true } when conditions are met, { held: false } otherwise
29
+ * Tracks whether a specific key (with optional modifiers) is being held down.
30
+ * Emits only when state changes.
35
31
  *
36
32
  * @example
37
33
  * ```typescript
38
- * // Track if Ctrl+Z or Cmd+Z is held
39
- * const isZoomModeHeld$ = keyboardHeld(window, {
40
- * key: 'z',
41
- * modifiers: ['meta', 'ctrl']
42
- * });
43
- *
44
- * // Multiple calls on same target share the keyboard stream
45
- * const isXHeld$ = keyboardHeld(window, { key: 'x' });
34
+ * // Prefer: use non-modifier key for reliable hold detection
35
+ * const spaceHeld$ = keyboardHeld(window, { key: 'Space' });
46
36
  *
47
- * // Use with when() operator
48
- * pipe(
49
- * wheel(element),
50
- * when(isZoomModeHeld$)
51
- * ).subscribe(...)
37
+ * // Caution: modifier combos may miss keyup on macOS
38
+ * const cmdZHeld$ = keyboardHeld(window, { key: 'z', modifiers: ['meta'] });
52
39
  * ```
53
40
  */
54
41
  export declare function keyboardHeld(target: EventTarget, options: KeyboardHeldOptions): Stream<HeldSignal>;
42
+ export {};
55
43
  //# sourceMappingURL=keyboard-held.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"keyboard-held.d.ts","sourceRoot":"","sources":["../../../src/browser/keyboard/keyboard-held.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAEnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAGnD,OAAO,EAAY,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAG3D,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,UAAW,SAAQ,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC;CAAG;AAEhE,eAAO,MAAM,gBAAgB,EAAG,MAAe,CAAC;AAEhD,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;OAGG;IACH,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC;CAC3B;AAiBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,mBAAmB,GAC3B,MAAM,CAAC,UAAU,CAAC,CAkDpB"}
1
+ {"version":3,"file":"keyboard-held.d.ts","sourceRoot":"","sources":["../../../src/browser/keyboard/keyboard-held.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAEnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAEnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAIjD,UAAU,SAAS;IACjB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,UAAW,SAAQ,MAAM,CAAC,eAAe,EAAE,SAAS,CAAC;CAAG;AAEzE,eAAO,MAAM,gBAAgB,EAAG,eAAwB,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC;CAC3B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,mBAAmB,GAC3B,MAAM,CAAC,UAAU,CAAC,CA+EpB"}
@@ -2,39 +2,21 @@ import { Stream } from '../../core/stream.js';
2
2
  import { KeyboardSignal } from './keyboard-signal.js';
3
3
  export type ModifierKey = "meta" | "ctrl" | "alt" | "shift";
4
4
  export interface KeyboardOptions {
5
- /**
6
- * Filter by key value(s). Uses OR logic if array.
7
- * Comparison is case-insensitive.
8
- * @example 'z' or ['+', '-', '=']
9
- */
5
+ /** Filter by key value(s). Case-insensitive. Uses OR logic if array. */
10
6
  key?: string | string[];
11
- /**
12
- * Filter by modifier keys. Uses OR logic (matches if any is pressed).
13
- * @example ['meta', 'ctrl'] - matches if metaKey OR ctrlKey is pressed
14
- */
7
+ /** Filter by modifier keys. Uses OR logic. */
15
8
  modifiers?: ModifierKey[];
16
- /**
17
- * If true, calls preventDefault() on matching events.
18
- * @default false
19
- */
9
+ /** If true, calls preventDefault() on matching events. @default false */
20
10
  preventDefault?: boolean;
11
+ /** If true, allows repeated keydown events. @default false */
12
+ allowRepeat?: boolean;
21
13
  }
22
14
  /**
23
- * Creates a stream of keyboard signals from keydown and keyup events on the target.
24
- * Optionally filters by key and/or modifier keys.
15
+ * Creates a keyboard signal stream. Shares underlying listeners per EventTarget.
25
16
  *
26
17
  * @example
27
- * ```typescript
28
- * // All keyboard events
29
- * keyboard(window).subscribe(signal => {
30
- * console.log(signal.value.key, signal.value.phase);
31
- * });
32
- *
33
- * // Only +/- keys with Ctrl or Cmd
34
- * keyboard(window, { key: ['+', '-'], modifiers: ['meta', 'ctrl'] }).subscribe(signal => {
35
- * // zoom in/out
36
- * });
37
- * ```
18
+ * keyboard(window).subscribe(signal => console.log(signal.value.key));
19
+ * keyboard(window, { key: 'z', modifiers: ['meta'] }).subscribe(handleUndo);
38
20
  */
39
21
  export declare function keyboard(target: EventTarget, options?: KeyboardOptions): Stream<KeyboardSignal>;
40
22
  //# sourceMappingURL=keyboard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"keyboard.d.ts","sourceRoot":"","sources":["../../../src/browser/keyboard/keyboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAiC,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE1F,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;AAE5D,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAExB;;;OAGG;IACH,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC;IAE1B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,MAAM,CAAC,cAAc,CAAC,CA2D/F"}
1
+ {"version":3,"file":"keyboard.d.ts","sourceRoot":"","sources":["../../../src/browser/keyboard/keyboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;AAE5D,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACxB,8CAA8C;IAC9C,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC;IAC1B,yEAAyE;IACzE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,MAAM,CAAC,cAAc,CAAC,CA2D/F"}
@@ -0,0 +1,7 @@
1
+ import { Stream } from '../../core/stream.js';
2
+ import { KeyboardSignal } from './keyboard-signal.js';
3
+ /** Shared keyboard stream for the given target. */
4
+ export declare function getSharedKeyboard(target: EventTarget): Stream<KeyboardSignal>;
5
+ /** Shared keyboard stream filtered by a specific key. Filters out repeated events. */
6
+ export declare function getSharedKeyboardForKey(target: EventTarget, key: string): Stream<KeyboardSignal>;
7
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/browser/keyboard/shared.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAEjE,OAAO,EAAiC,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAyB1F,mDAAmD;AACnD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,cAAc,CAAC,CAO7E;AAED,sFAAsF;AACtF,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,CA4BhG"}
@@ -9,7 +9,9 @@ export interface Signal<K extends string = string, V = unknown> {
9
9
  * Utility type to extend a Signal's value type with additional properties.
10
10
  * Used by operators that add computed properties to signals.
11
11
  */
12
- export type ExtendSignalValue<T extends Signal, Additional> = Signal<T["kind"], T["value"] & Additional>;
12
+ export type ExtendSignalValue<T extends Signal, Additional> = Omit<T, "value"> & {
13
+ readonly value: T["value"] & Additional;
14
+ };
13
15
  /**
14
16
  * Utility type to constrain a Signal to have specific value properties.
15
17
  * Used by operators that require certain properties in the input signal.
@@ -1 +1 @@
1
- {"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["../../src/core/signal.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,MAAM,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,OAAO;IAC5D,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,MAAM,EAAE,UAAU,IAAI,MAAM,CAClE,CAAC,CAAC,MAAM,CAAC,EACT,CAAC,CAAC,OAAO,CAAC,GAAG,UAAU,CACxB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AAE9C,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,OAAO,EACjE,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,CAAC,GACP,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAOd"}
1
+ {"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["../../src/core/signal.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,MAAM,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,OAAO;IAC5D,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,MAAM,EAAE,UAAU,IAAI,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG;IAC/E,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,UAAU,CAAC;CACzC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AAE9C,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,OAAO,EACjE,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,CAAC,GACP,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAOd"}
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("./stream-Cp6eCZs2.cjs"),h=require("./recognizer-DbiEtBOM.cjs"),k=require("./share-DT0L7vCb.cjs"),Y=require("./types-BGR5Mhw9.cjs"),z=require("./single-pointer/pointer.cjs"),C="dom-event";function S(e){return h.createSignal(C,e)}function H(e,t,o){return f.createStream(r=>{const n=i=>{r.next(S(i))};return e.addEventListener(t,n,o),()=>{e.removeEventListener(t,n,o)}})}const y=["mousedown","mousemove","mouseup"];function M(e,t){return f.createStream(o=>{const r=n=>{o.next(S(n))};for(let n=0;n<y.length;n++){const i=y[n];e.addEventListener(i,r,t)}return()=>{for(let n=0;n<y.length;n++){const i=y[n];e.removeEventListener(i,r,t)}}})}const p=["pointerdown","pointermove","pointerup","pointercancel"];function v(e,t){return f.createStream(o=>{const r=n=>{o.next(S(n))};for(let n=0;n<p.length;n++){const i=p[n];e.addEventListener(i,r,t)}return()=>{for(let n=0;n<p.length;n++){const i=p[n];e.removeEventListener(i,r,t)}}})}const E=["touchstart","touchmove","touchend","touchcancel"];function G(e,t){return f.createStream(o=>{const r=n=>{o.next(S(n))};for(let n=0;n<E.length;n++){const i=E[n];e.addEventListener(i,r,t)}return()=>{for(let n=0;n<E.length;n++){const i=E[n];e.removeEventListener(i,r,t)}}})}const P="keyboard";function I(e){return h.createSignal(P,e)}function g(e,t){return I({phase:t,key:e.key,code:e.code,repeat:e.repeat,altKey:e.altKey,ctrlKey:e.ctrlKey,metaKey:e.metaKey,shiftKey:e.shiftKey,originalEvent:e})}function N(e,t){const o=t?.key?Array.isArray(t.key)?t.key.map(s=>s.toLowerCase()):[t.key.toLowerCase()]:null,r=t?.modifiers,n=t?.preventDefault??!1,i=s=>o?o.includes(s.toLowerCase()):!0,a=s=>!r||r.length===0?!0:r.some(u=>{switch(u){case"meta":return s.metaKey;case"ctrl":return s.ctrlKey;case"alt":return s.altKey;case"shift":return s.shiftKey;default:return!1}}),c=s=>i(s.key)&&a(s);return f.createStream(s=>{const u=m=>{const l=m;c(l)&&(n&&l.preventDefault(),s.next(g(l,"down")))},d=m=>{const l=m;c(l)&&(n&&l.preventDefault(),s.next(g(l,"up")))};return e.addEventListener("keydown",u),e.addEventListener("keyup",d),()=>{e.removeEventListener("keydown",u),e.removeEventListener("keyup",d)}})}const D="held",L=new WeakMap;function O(e){let t=L.get(e);return t||(t=k.share()(N(e)),L.set(e,t)),t}function X(e,t){const{key:o,modifiers:r}=t,n=o.toLowerCase(),i=O(e),a=c=>!r||r.length===0?!0:r.some(s=>{switch(s){case"meta":return c.metaKey;case"ctrl":return c.ctrlKey;case"alt":return c.altKey;case"shift":return c.shiftKey;default:return!1}});return f.createStream(c=>{let s=!1;return i.subscribe({next(u){try{const{key:d,phase:m}=u.value;d.toLowerCase()===n&&(s=m==="down");const l=a(u.value),W=s&&l;c.next(h.createSignal(D,{held:W}))}catch(d){c.error?.(d)}},error:c.error?.bind(c),complete:c.complete?.bind(c)})})}const R="multi-pointer";function F(e){return h.createSignal(R,e)}function q(e,t={}){const o={maxPointers:t.maxPointers??2},r=new Map;let n="idle";function i(a,c){return a===0?n==="active"&&c>0?"ended":"idle":"active"}return{process(a){const c=e(a,r,o);if(c===null)return null;const s=c.endedPointerIds;for(const l of s)r.delete(l);const u=Array.from(r.values()),d=i(u.length,s.length);n=d==="ended"?"idle":d;const m={phase:d,pointers:u,count:u.length};return F(m)},get isActive(){return r.size>0},get activeCount(){return r.size},reset(){r.clear(),n="idle"},dispose(){this.reset()}}}function U(e={}){function t(o,r,n){const i=o.value,a=`${i.pointerType}-${i.pointerId}`,c=[];switch(i.type){case"pointerdown":{if(r.size>=n.maxPointers)return null;r.set(a,K(i,"start"));break}case"pointermove":{if(!r.has(a))return null;r.set(a,K(i,"move"));break}case"pointerup":{if(!r.has(a))return null;r.set(a,K(i,"end")),c.push(a);break}case"pointercancel":{if(!r.has(a))return null;r.set(a,K(i,"cancel")),c.push(a);break}default:return null}return{pointers:r,endedPointerIds:c}}return q(t,e)}function _(e={}){return t=>f.createStream(o=>{const r=U(e),n=t.subscribe({next(i){const a=r.process(i);a&&o.next(a)},error(i){o.error?.(i)},complete(){o.complete?.()}});return()=>{n(),r.dispose()}})}function K(e,t){const o=t==="start"||t==="end"?Y.toSinglePointerButton(e.button):"none";return{id:`${e.pointerType}-${e.pointerId}`,phase:t,x:e.clientX,y:e.clientY,pageX:e.pageX,pageY:e.pageY,pointerType:V(e.pointerType),button:o,pressure:e.pressure}}function V(e){switch(e){case"mouse":return"mouse";case"touch":return"touch";case"pen":return"pen";default:return"unknown"}}function $(e,t={}){const o=v(e);return _(t)(o)}function B(e,t={}){const o=v(e);return z.singlePointerRecognizer(t)(o)}const x="wheel";function b(e){return h.createSignal(x,e)}function Z(e){switch(e){case 0:return"pixel";case 1:return"line";case 2:return"page";default:return"pixel"}}function A(e){return b({deltaX:e.deltaX,deltaY:e.deltaY,deltaZ:e.deltaZ,deltaMode:Z(e.deltaMode),x:e.clientX,y:e.clientY,pageX:e.pageX,pageY:e.pageY,altKey:e.altKey,ctrlKey:e.ctrlKey,metaKey:e.metaKey,shiftKey:e.shiftKey,originalEvent:e})}const w=new WeakMap;function j(e,t){let o=w.get(e);o||(o=new Map,w.set(e,o));let r=o.get(t);return r||(r=k.share()(J(e,t)),o.set(t,r)),r}function J(e,t){return f.createStream(o=>{const r=n=>{o.next(A(n))};return e.addEventListener("wheel",r,{passive:t}),()=>{e.removeEventListener("wheel",r)}})}function Q(e,t){const o=t?.passive??!0,r=t?.modifiers,n=t?.preventDefault??!1,i=j(e,o);if(!r?.length&&!n)return i;const a=c=>!r||r.length===0?!0:r.some(s=>{switch(s){case"meta":return c.metaKey;case"ctrl":return c.ctrlKey;case"alt":return c.altKey;case"shift":return c.shiftKey;default:return!1}});return f.createStream(c=>i.subscribe({next(s){try{a(s.value)&&(n&&s.value.originalEvent.preventDefault(),c.next(s))}catch(u){c.error?.(u)}},error:c.error?.bind(c),complete:c.complete?.bind(c)}))}function ee(...e){return t=>T(t,...e)}function T(e,...t){return t.reduce((o,r)=>r(o),e)}exports.createStream=f.createStream;exports.createSignal=h.createSignal;exports.setCerebDeviceId=h.setCerebDeviceId;exports.HELD_SIGNAL_KIND=D;exports.KEYBOARD_SIGNAL_KIND=P;exports.WHEEL_SIGNAL_KIND=x;exports.compose=ee;exports.createKeyboardSignal=I;exports.createKeyboardSignalFromEvent=g;exports.createWheelSignal=b;exports.createWheelSignalFromEvent=A;exports.domEvent=H;exports.keyboard=N;exports.keyboardHeld=X;exports.mouseEvents=M;exports.multiPointer=$;exports.multiPointerFromPointer=_;exports.pipe=T;exports.pointerEvents=v;exports.singlePointer=B;exports.touchEvents=G;exports.wheel=Q;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("./stream-Cp6eCZs2.cjs"),m=require("./recognizer-DbiEtBOM.cjs"),I=require("./share-DT0L7vCb.cjs"),F=require("./types-BGR5Mhw9.cjs"),q=require("./single-pointer/pointer.cjs"),$="dom-event";function L(e){return m.createSignal($,e)}function U(e,r,o){return d.createStream(t=>{const n=a=>{t.next(L(a))};return e.addEventListener(r,n,o),()=>{e.removeEventListener(r,n,o)}})}const K=["mousedown","mousemove","mouseup"];function V(e,r){return d.createStream(o=>{const t=n=>{o.next(L(n))};for(let n=0;n<K.length;n++){const a=K[n];e.addEventListener(a,t,r)}return()=>{for(let n=0;n<K.length;n++){const a=K[n];e.removeEventListener(a,t,r)}}})}const S=["pointerdown","pointermove","pointerup","pointercancel"];function N(e,r){return d.createStream(o=>{const t=n=>{o.next(L(n))};for(let n=0;n<S.length;n++){const a=S[n];e.addEventListener(a,t,r)}return()=>{for(let n=0;n<S.length;n++){const a=S[n];e.removeEventListener(a,t,r)}}})}const E=["touchstart","touchmove","touchend","touchcancel"];function B(e,r){return d.createStream(o=>{const t=n=>{o.next(L(n))};for(let n=0;n<E.length;n++){const a=E[n];e.addEventListener(a,t,r)}return()=>{for(let n=0;n<E.length;n++){const a=E[n];e.removeEventListener(a,t,r)}}})}const _="keyboard";function M(e){return m.createSignal(_,e)}function P(e,r){return M({phase:r,key:e.key,code:e.code,repeat:e.repeat,altKey:e.altKey,ctrlKey:e.ctrlKey,metaKey:e.metaKey,shiftKey:e.shiftKey,originalEvent:e})}const b=new WeakMap,x=new WeakMap;function Z(e){return d.createStream(r=>{const o=n=>{r.next(P(n,"down"))},t=n=>{r.next(P(n,"up"))};return e.addEventListener("keydown",o),e.addEventListener("keyup",t),()=>{e.removeEventListener("keydown",o),e.removeEventListener("keyup",t)}})}function w(e){let r=b.get(e);return r||(r=I.share()(Z(e)),b.set(e,r)),r}function T(e,r){const o=r.toLowerCase();let t=x.get(e);t||(t=new Map,x.set(e,t));let n=t.get(o);if(!n){const a=w(e),s=d.createStream(i=>a.subscribe({next(u){u.value.repeat||u.value.key.toLowerCase()===o&&i.next(u)},error:i.error?.bind(i),complete:i.complete?.bind(i)}));n=I.share()(s),t.set(o,n)}return n}function j(e,r){if(!r)return w(e);const o=r.modifiers,t=r.preventDefault??!1,n=r.allowRepeat??!1,a=typeof r.key=="string",s=r.key&&Array.isArray(r.key)?r.key.map(c=>c.toLowerCase()):null,i=a?T(e,r.key):w(e);if(a&&!o&&!t&&!n)return i;const u=c=>s?s.includes(c.toLowerCase()):!0,f=c=>!o||o.length===0?!0:o.some(l=>{switch(l){case"meta":return c.metaKey;case"ctrl":return c.ctrlKey;case"alt":return c.altKey;case"shift":return c.shiftKey;default:return!1}});return d.createStream(c=>i.subscribe({next(l){const{key:y,repeat:k}=l.value;!n&&k||u(y)&&f(l.value)&&(t&&l.value.originalEvent.preventDefault(),c.next(l))},error:c.error?.bind(c),complete:c.complete?.bind(c)}))}const A="keyboard-held";function J(e,r){const{key:o,modifiers:t}=r,n=o.toLowerCase(),s=t&&t.length>0?w(e):T(e,o),i=new Set;if(t)for(const c of t)c==="meta"&&i.add("meta"),c==="ctrl"&&i.add("control"),c==="alt"&&i.add("alt"),c==="shift"&&i.add("shift");const u=c=>i.has(c.toLowerCase()),f=c=>!t||t.length===0?!0:t.some(l=>{switch(l){case"meta":return c.metaKey;case"ctrl":return c.ctrlKey;case"alt":return c.altKey;case"shift":return c.shiftKey;default:return!1}});return d.createStream(c=>{let l=!1,y;const k=h=>{h!==y&&(y=h,c.next(m.createSignal(A,{opened:h})))};return s.subscribe({next(h){try{const{key:p,phase:v,repeat:z}=h.value;if(z)return;const G=p.toLowerCase()===n,O=u(p);G?l=v==="down":(v==="down"||O&&v==="up")&&(l=!1);const X=f(h.value);k(l&&X)}catch(p){c.error?.(p)}},error:c.error?.bind(c),complete:c.complete?.bind(c)})})}const Q="multi-pointer";function ee(e){return m.createSignal(Q,e)}function te(e,r={}){const o={maxPointers:r.maxPointers??2},t=new Map;let n="idle";function a(s,i){return s===0?n==="active"&&i>0?"ended":"idle":"active"}return{process(s){const i=e(s,t,o);if(i===null)return null;const u=i.endedPointerIds;for(const y of u)t.delete(y);const f=Array.from(t.values()),c=a(f.length,u.length);n=c==="ended"?"idle":c;const l={phase:c,pointers:f,count:f.length};return ee(l)},get isActive(){return t.size>0},get activeCount(){return t.size},reset(){t.clear(),n="idle"},dispose(){this.reset()}}}function re(e={}){function r(o,t,n){const a=o.value,s=`${a.pointerType}-${a.pointerId}`,i=[];switch(a.type){case"pointerdown":{if(t.size>=n.maxPointers)return null;t.set(s,g(a,"start"));break}case"pointermove":{if(!t.has(s))return null;t.set(s,g(a,"move"));break}case"pointerup":{if(!t.has(s))return null;t.set(s,g(a,"end")),i.push(s);break}case"pointercancel":{if(!t.has(s))return null;t.set(s,g(a,"cancel")),i.push(s);break}default:return null}return{pointers:t,endedPointerIds:i}}return te(r,e)}function C(e={}){return r=>d.createStream(o=>{const t=re(e),n=r.subscribe({next(a){const s=t.process(a);s&&o.next(s)},error(a){o.error?.(a)},complete(){o.complete?.()}});return()=>{n(),t.dispose()}})}function g(e,r){const o=r==="start"||r==="end"?F.toSinglePointerButton(e.button):"none";return{id:`${e.pointerType}-${e.pointerId}`,phase:r,x:e.clientX,y:e.clientY,pageX:e.pageX,pageY:e.pageY,pointerType:ne(e.pointerType),button:o,pressure:e.pressure}}function ne(e){switch(e){case"mouse":return"mouse";case"touch":return"touch";case"pen":return"pen";default:return"unknown"}}function oe(e,r={}){const o=N(e);return C(r)(o)}function ce(e,r={}){const o=N(e);return q.singlePointerRecognizer(r)(o)}const W="wheel";function H(e){return m.createSignal(W,e)}function ae(e){switch(e){case 0:return"pixel";case 1:return"line";case 2:return"page";default:return"pixel"}}function R(e){return H({deltaX:e.deltaX,deltaY:e.deltaY,deltaZ:e.deltaZ,deltaMode:ae(e.deltaMode),x:e.clientX,y:e.clientY,pageX:e.pageX,pageY:e.pageY,altKey:e.altKey,ctrlKey:e.ctrlKey,metaKey:e.metaKey,shiftKey:e.shiftKey,originalEvent:e})}const D=new WeakMap;function ie(e,r){let o=D.get(e);o||(o=new Map,D.set(e,o));let t=o.get(r);return t||(t=I.share()(se(e,r)),o.set(r,t)),t}function se(e,r){return d.createStream(o=>{const t=n=>{o.next(R(n))};return e.addEventListener("wheel",t,{passive:r}),()=>{e.removeEventListener("wheel",t)}})}function ue(e,r){const o=r?.passive??!0,t=r?.modifiers,n=r?.preventDefault??!1,a=ie(e,o);if(!t?.length&&!n)return a;const s=i=>!t||t.length===0?!0:t.some(u=>{switch(u){case"meta":return i.metaKey;case"ctrl":return i.ctrlKey;case"alt":return i.altKey;case"shift":return i.shiftKey;default:return!1}});return d.createStream(i=>a.subscribe({next(u){try{s(u.value)&&(n&&u.value.originalEvent.preventDefault(),i.next(u))}catch(f){i.error?.(f)}},error:i.error?.bind(i),complete:i.complete?.bind(i)}))}function le(...e){return r=>Y(r,...e)}function Y(e,...r){return r.reduce((o,t)=>t(o),e)}exports.createStream=d.createStream;exports.createSignal=m.createSignal;exports.setCerebDeviceId=m.setCerebDeviceId;exports.HELD_SIGNAL_KIND=A;exports.KEYBOARD_SIGNAL_KIND=_;exports.WHEEL_SIGNAL_KIND=W;exports.compose=le;exports.createKeyboardSignal=M;exports.createKeyboardSignalFromEvent=P;exports.createWheelSignal=H;exports.createWheelSignalFromEvent=R;exports.domEvent=U;exports.keyboard=j;exports.keyboardHeld=J;exports.mouseEvents=V;exports.multiPointer=oe;exports.multiPointerFromPointer=C;exports.pipe=Y;exports.pointerEvents=N;exports.singlePointer=ce;exports.touchEvents=B;exports.wheel=ue;
2
2
  //# sourceMappingURL=index.cjs.map