|
1 | | -[![test][test-badge]][test] |
| 1 | +[![test][test-badge]][test] |
2 | 2 |
|
3 | | -# pointerdriver |
| 3 | +# pointerdriver |
4 | 4 |
|
5 | | -> Synthesizes and dispatches events. |
6 | | -> Mimics the event stream as if it were executed |
7 | | -> by an actual human driving the interaction. |
| 5 | +Synthesize pointer, touch, and gesture events on any page. |
8 | 6 |
|
9 | | -```sh |
10 | | -npm i github:TheProfs/pointerdriver |
| 7 | +```bash |
| 8 | +npx github:TheProfs/pointerdriver |
11 | 9 | ``` |
12 | 10 |
|
13 | | -## Usage |
| 11 | +> [!NOTE] |
| 12 | +> LLM Agents **must** read the `pointerdriver` skill first. |
| 13 | +> Ask your LLM to run: `pointerdriver --skill` and study it carefully. |
| 14 | +
|
| 15 | +## How it works: |
| 16 | + |
| 17 | +1. Run your project however you want. |
| 18 | + We're assuming it's running on `localhost:3000` |
| 19 | +2. Run a pointerdriver server with `npx github:TheProfs/pointerdriver` |
| 20 | +3. Go in your browser console and run: |
14 | 21 |
|
15 | 22 | ```js |
16 | | -import { PointerDriver } from 'pointerdriver' |
| 23 | +const { |
| 24 | + DragMotion, GlideMotion, StrokeMotion, |
| 25 | + PinchMotion, TwistMotion, SwipeMotion |
| 26 | +} = await import('http://127.0.0.1:5619/pointerdriver.js') |
| 27 | +``` |
| 28 | + |
| 29 | +This loads `pointerdriver` and allows executing any of the following motions: |
| 30 | + |
| 31 | +## Motions |
| 32 | + |
| 33 | +Create a motion by running: `new Motion(arguments).perform()`. |
| 34 | + |
| 35 | +A motion is a *human-maneuver*; |
| 36 | +Regardless of the arguments you use for it, a motion eventually |
| 37 | +generates the exact sequence of events that would be generated |
| 38 | +from a real device. |
| 39 | + |
17 | 40 |
|
18 | | -const driver = new PointerDriver('#el') |
| 41 | +### `DragMotion(el, points)` |
19 | 42 |
|
20 | | -// Pencil |
21 | | -await driver.stroke([[30, 50, 0], [60, 80, 16]]) // pen drag |
| 43 | +Mouse drag across a surface. |
22 | 44 |
|
23 | | -// Mouse |
24 | | -await driver.drag([[30, 50, 0], [60, 80, 16]]) // mouse drag |
| 45 | +| Param | Description | |
| 46 | +|----------|--------------------------------------------| |
| 47 | +| `el` | target element | |
| 48 | +| `points` | `[x, y, ms][]`; `ms` monotonic and `>= 0` | |
25 | 49 |
|
26 | | -// Touch |
27 | | -await driver.glide([[ |
28 | | - 30, 50, 0], [60, 80, 16] |
29 | | -]]) // one-finger drag |
30 | | -await driver.pinch(2) // two-finger pinch |
31 | | -await driver.swipe(80, 0) // two-finger swipe |
32 | | -await driver.twist() // two-finger twist |
| 50 | +```js |
| 51 | +await new DragMotion(document.querySelector('#el'), [ |
| 52 | + [30, 50, 0], |
| 53 | + [60, 80, 16], |
| 54 | +]).perform() |
33 | 55 | ``` |
34 | 56 |
|
35 | | -### Text strokes |
| 57 | +### `GlideMotion(el, points)` |
| 58 | + |
| 59 | +Finger draw on a surface. |
36 | 60 |
|
37 | | -`stroke()` accepts a string, |
38 | | -but you must provide an SVG font URL in the constructor. |
| 61 | +| Param | Description | |
| 62 | +|----------|--------------------------------------------| |
| 63 | +| `el` | target element | |
| 64 | +| `points` | `[x, y, ms][]`; `ms` monotonic and `>= 0` | |
39 | 65 |
|
40 | 66 | ```js |
41 | | -const driver = new PointerDriver('#el', { |
42 | | - font: 'http://127.0.0.1:5619/fonts/EMS_Elfin_Smooth.svg', |
43 | | -}) |
| 67 | +await new GlideMotion(document.querySelector('#el'), [ |
| 68 | + [30, 50, 0], |
| 69 | + [60, 80, 16], |
| 70 | +]).perform() |
| 71 | +``` |
| 72 | + |
| 73 | +### `StrokeMotion(el, points)` |
| 74 | + |
| 75 | +Pen stylus stroke on a surface. |
44 | 76 |
|
45 | | -await driver.stroke('Hello', { fontSize: 48 }) |
| 77 | +| Param | Description | |
| 78 | +|----------|--------------------------------------------| |
| 79 | +| `el` | target element | |
| 80 | +| `points` | `[x, y, ms][]`; `ms` monotonic and `>= 0` | |
| 81 | + |
| 82 | +```js |
| 83 | +await new StrokeMotion(document.querySelector('#el'), [ |
| 84 | + [30, 50, 0], |
| 85 | + [60, 80, 16], |
| 86 | +]).perform() |
46 | 87 | ``` |
47 | 88 |
|
48 | | -## Server |
| 89 | +### `PinchMotion(el, scale, { x, y, distance, steps })` |
49 | 90 |
|
50 | | -Run a local static server: |
| 91 | +Two-finger pinch together or apart. |
51 | 92 |
|
52 | | -```bash |
53 | | -npx github:TheProfs/pointerdriver |
| 93 | +| Param | Default | Description | |
| 94 | +|------------|---------|--------------------------| |
| 95 | +| `el` | | target element | |
| 96 | +| `scale` | | target scale factor | |
| 97 | +| `x`, `y` | | gesture center | |
| 98 | +| `distance` | `100` | initial finger gap in px | |
| 99 | +| `steps` | `20` | interpolation frames | |
| 100 | + |
| 101 | +```js |
| 102 | +await new PinchMotion(document.querySelector('#el'), 2, { |
| 103 | + x: 60, y: 80 |
| 104 | +}).perform() |
| 105 | +``` |
| 106 | + |
| 107 | +### `TwistMotion(el, degrees, { x, y, radius, steps })` |
| 108 | + |
| 109 | +Two-finger rotation around a center point. |
| 110 | + |
| 111 | +| Param | Default | Description | |
| 112 | +|-----------|---------|-----------------------------| |
| 113 | +| `el` | | target element | |
| 114 | +| `degrees` | `45` | rotation angle | |
| 115 | +| `x`, `y` | | gesture center | |
| 116 | +| `radius` | `80` | finger distance from center | |
| 117 | +| `steps` | `20` | interpolation frames | |
| 118 | + |
| 119 | +```js |
| 120 | +await new TwistMotion(document.querySelector('#el'), 45, { |
| 121 | + x: 60, y: 80 |
| 122 | +}).perform() |
| 123 | +``` |
| 124 | + |
| 125 | +### `SwipeMotion(el, distance, { x, y, angle, separation, steps })` |
| 126 | + |
| 127 | +Two-finger parallel drag. |
| 128 | + |
| 129 | +| Param | Default | Description | |
| 130 | +|--------------|---------|---------------------------| |
| 131 | +| `el` | | target element | |
| 132 | +| `distance` | | travel distance in px | |
| 133 | +| `x`, `y` | | gesture center | |
| 134 | +| `angle` | `0` | direction in degrees | |
| 135 | +| `separation` | `40` | gap between fingers in px | |
| 136 | +| `steps` | `20` | interpolation frames | |
| 137 | + |
| 138 | +```js |
| 139 | +await new SwipeMotion(document.querySelector('#el'), 200, { |
| 140 | + x: 60, y: 80 |
| 141 | +}).perform() |
54 | 142 | ``` |
55 | 143 |
|
56 | | -Open `http://127.0.0.1:5619/`. |
| 144 | +## Notes: |
| 145 | + |
| 146 | +- The module server only serves `/pointerdriver.js` and `/src/*/index.js`. |
| 147 | +- It sets permissive CORS headers so you can import it from any page. |
| 148 | + |
| 149 | +## HTTPS pages |
| 150 | + |
| 151 | +If the target page is HTTPS, |
| 152 | +importing pointerdriver from HTTP is blocked as mixed content. |
| 153 | +Serve pointerdriver over HTTPS (see `bin/skill.md` for one approach). |
| 154 | + |
57 | 155 |
|
58 | | -## Test |
| 156 | +## Run tests |
59 | 157 |
|
60 | 158 | ```bash |
61 | 159 | npm test |
62 | 160 | ``` |
63 | 161 |
|
64 | | -## License |
| 162 | +## License |
65 | 163 |
|
66 | | -MIT |
| 164 | +[MIT][license] |
67 | 165 |
|
68 | | -[test-badge]: https://github.com/TheProfs/pointerdriver/actions/workflows/test.yml/badge.svg |
69 | | -[test]: https://github.com/TheProfs/pointerdriver/actions/workflows/test.yml |
| 166 | +[test-badge]: https://github.com/TheProfs/pointerdriver/actions/workflows/test.yml/badge.svg |
| 167 | +[test]: https://github.com/TheProfs/pointerdriver/actions/workflows/test.yml |
| 168 | +[license]: https://opensource.org/licenses/MIT |
0 commit comments