@humanjs/playwright 0.2.0 → 0.3.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/dist/index.d.ts CHANGED
@@ -1,6 +1,196 @@
1
- import { PersonalityConfig, HumanPlugin, Point, Personality } from '@humanjs/core';
2
- export { ActionResult, ActionType, BezierPathOptions, DwellProfile, HumanAction, HumanPlugin, HumanizePathOptions, KnownActionType, MouseProfile, Personality, PersonalityConfig, PersonalityExtension, PluginContext, Point, PresetName, ReadingProfile, Rng, TypingProfile, applyMicroJitter, applyVelocityProfile, bezierPath, blend, careful, createRng, distracted, fast, humanizePath, precise, resolvePersonality } from '@humanjs/core';
3
- import { Locator, Page } from 'playwright';
1
+ import { ReadKind, PersonalityConfig, HumanPlugin, Point, Personality } from '@humanjs/core';
2
+ export { ActionResult, ActionType, BezierPathOptions, ComputeReadingDwellOptions, DwellProfile, HumanAction, HumanPlugin, HumanizePathOptions, Keystroke, KnownActionType, MouseProfile, Personality, PersonalityConfig, PersonalityExtension, PlanTypingOptions, PluginContext, Point, PresetName, ReadKind, ReadingProfile, Rng, ScrollProfile, ScrollSegment, TypingProfile, applyMicroJitter, applyVelocityProfile, bezierPath, blend, careful, computeReadingDwellMs, countWords, createRng, distracted, fast, humanizePath, planScroll, planTypeKeystrokes, precise, resolvePersonality } from '@humanjs/core';
3
+ import { Locator, BrowserContext, Page } from 'playwright';
4
+
5
+ /**
6
+ * What to read:
7
+ * - `string`: a Playwright-compatible selector (matches `click()` / `type()`).
8
+ * The element's `innerText` is resolved and word-counted.
9
+ * - `Locator`: same, but you already have a Locator handle.
10
+ * - `{ text }`: literal text (you have the string in hand, no selector).
11
+ * - `{ words }`: pre-counted (bypass extraction entirely).
12
+ */
13
+ type ReadTarget = string | Locator | {
14
+ readonly text: string;
15
+ } | {
16
+ readonly words: number;
17
+ };
18
+ interface ReadOptions {
19
+ /**
20
+ * Reading mode. Built-in multipliers on top of `personality.reading.wpm`:
21
+ * - `'prose'`: 1.0× (default for non-code targets)
22
+ * - `'code'`: 0.4× (default when target is a `<pre>` or `<code>` element)
23
+ * - `'scan'`: 1.8× (skim mode, must be explicit)
24
+ *
25
+ * **Smart defaults:** when `kind` is omitted AND the target is a Locator/
26
+ * selector, the adapter inspects the resolved element's tag — `<pre>` and
27
+ * `<code>` auto-detect as `'code'`; everything else as `'prose'`. An
28
+ * explicit `kind` always wins over auto-detection.
29
+ */
30
+ readonly kind?: ReadKind;
31
+ /** Override the effective WPM multiplier directly. Wins over `kind`. */
32
+ readonly wpmMultiplier?: number;
33
+ /**
34
+ * For selector/Locator targets: scroll the element into the viewport
35
+ * before the dwell. A user can't read what isn't on screen. Defaults to
36
+ * `false` — in most flows the caller has already scrolled there.
37
+ */
38
+ readonly scrollIntoView?: boolean;
39
+ /**
40
+ * For selector/Locator targets: trace a humanized cursor path through the
41
+ * target's bounding box while the dwell elapses, like an eye tracking
42
+ * lines of text. Off by default — opt in when you want the humanization
43
+ * signal (or when reading-time mouseover behavior matters, e.g. tooltips
44
+ * that activate while a real user's cursor moves over the content).
45
+ *
46
+ * The scan path is generated by `planReadingScan` from `@humanjs/core`
47
+ * (same deterministic-by-seed contract as the rest of the library) and
48
+ * walked over the computed dwell duration, so motion finishes exactly as
49
+ * the read does. The cursor's tracked position is updated to the scan's
50
+ * endpoint so the next click starts from where the eye landed.
51
+ *
52
+ * Ignored when the target is `{ text }` or `{ words }` (no bounding box).
53
+ */
54
+ readonly withMotion?: boolean;
55
+ }
56
+ /** Outcome of a read, returned to the caller for observability. */
57
+ interface ReadResult {
58
+ /** Number of words counted (after whitespace splitting / from caller's `{ words }`). */
59
+ readonly words: number;
60
+ /** Sleep duration in ms. Zero in `speed: 'instant'` or for zero-word inputs. */
61
+ readonly durationMs: number;
62
+ /** Final reading kind used (after auto-detection + explicit-override resolution). */
63
+ readonly kind: ReadKind;
64
+ }
65
+
66
+ /**
67
+ * What / where to scroll. The chosen axis (`'y'` by default, `'x'` via
68
+ * `options.axis`) decides whether targets resolve along the vertical or
69
+ * horizontal axis.
70
+ *
71
+ * - `'natural'`: scroll one viewport along the chosen axis (default).
72
+ * - `'end'`: scroll to the far edge (bottom for `'y'`, right for `'x'`).
73
+ * - `'top'`: scroll to the origin (top for `'y'`, left for `'x'`).
74
+ * - `string`: a Playwright-compatible selector — scroll until in view.
75
+ * - `Locator`: same, but you already have a Locator handle.
76
+ * - `{ by: number }`: relative pixel delta (negative = up / left).
77
+ * - `{ to: number }`: absolute scroll position on the chosen axis.
78
+ */
79
+ type ScrollTarget = 'natural' | 'end' | 'top' | string | Locator | {
80
+ readonly by: number;
81
+ } | {
82
+ readonly to: number;
83
+ };
84
+ interface ScrollOptions {
85
+ /**
86
+ * Force an overshoot + correction even if the personality wouldn't choose
87
+ * one. Useful when you want the humanization signal regardless of
88
+ * personality (e.g. demos).
89
+ */
90
+ readonly overshoot?: boolean;
91
+ /**
92
+ * Disable mid-scroll micro-pauses. Defaults to `true` (pauses enabled).
93
+ * Set `false` for the smoothest possible motion.
94
+ */
95
+ readonly withPauses?: boolean;
96
+ /**
97
+ * For element targets only: where to align the element along the chosen
98
+ * axis when the scroll ends. Mirrors `scrollIntoView({ block })`.
99
+ * Defaults to `'start'`.
100
+ *
101
+ * - `'start'`: element's leading edge aligns with the viewport's leading
102
+ * edge (top for `axis: 'y'`, left for `axis: 'x'`)
103
+ * - `'center'`: element centers in the viewport
104
+ * - `'end'`: element's trailing edge aligns with the viewport's trailing
105
+ * edge (bottom for `'y'`, right for `'x'`)
106
+ * - `'nearest'`: do the minimum scroll — stay put if the element is
107
+ * already fully visible, otherwise scroll to the closest edge
108
+ */
109
+ readonly block?: 'start' | 'center' | 'end' | 'nearest';
110
+ /**
111
+ * Scroll inside a scrollable container instead of the page. Accepts a
112
+ * Playwright-compatible selector or a built `Locator`. Every scroll
113
+ * semantic (`'natural'`, `'top'`, `'end'`, `{ by }`, `{ to }`, element
114
+ * targets, `block` alignment) applies relative to the container.
115
+ *
116
+ * In humanized speed modes, the cursor parks over the container's
117
+ * center so the visible cursor reads as "human hand on the wheel"
118
+ * (when paired with `installMouseHelper`), and each planned segment
119
+ * advances the container's `scrollLeft` / `scrollTop` directly. Direct
120
+ * property assignment is more reliable than dispatching wheel events
121
+ * into nested overflow containers — Playwright's synthetic wheel
122
+ * events don't always route past the cursor element. In `'instant'`
123
+ * mode, the container's scroll position is set with a single
124
+ * `scrollTo` call.
125
+ *
126
+ * Common use: chat threads, modal bodies, infinite-scroll feeds, any
127
+ * `<div style="overflow-y: auto">` that owns its own scrollbar.
128
+ */
129
+ readonly within?: string | Locator;
130
+ /**
131
+ * Which axis to scroll along. Defaults to `'y'` (vertical).
132
+ *
133
+ * Set `'x'` for horizontal scrolling — carousels, kanban boards,
134
+ * sideways galleries. Every target shape still works: `'natural'`
135
+ * scrolls one viewport-width right, `'top'` jumps to scrollLeft 0,
136
+ * `'end'` to (scrollWidth - clientWidth), `{ by }` / `{ to }` apply to
137
+ * the X-axis, and element targets scroll until the element is visible
138
+ * horizontally.
139
+ */
140
+ readonly axis?: 'x' | 'y';
141
+ }
142
+ /** Outcome of a scroll, returned to the caller for observability. */
143
+ interface ScrollResult {
144
+ /** Starting scroll position along the chosen axis (scrollY or scrollX). */
145
+ readonly from: number;
146
+ /** Final scroll position the scroll aimed for, along the chosen axis. */
147
+ readonly to: number;
148
+ /** Signed pixel distance (`to - from`). */
149
+ readonly distance: number;
150
+ /** Total elapsed dwell + motion time in ms. Zero in `speed: 'instant'`. */
151
+ readonly durationMs: number;
152
+ }
153
+
154
+ /** Options for {@link installMouseHelper}. */
155
+ interface InstallMouseHelperOptions {
156
+ /** Cursor fill color. Defaults to the HumanJS amber `#f5a55c`. */
157
+ readonly color?: string;
158
+ /** Cursor visual size in pixels. Defaults to 22. */
159
+ readonly size?: number;
160
+ /**
161
+ * Render a ripple at each `mousedown` position so clicks read on video.
162
+ * Defaults to `true`.
163
+ */
164
+ readonly showClicks?: boolean;
165
+ /** Halo opacity behind the cursor. Defaults to `0.18`. */
166
+ readonly haloOpacity?: number;
167
+ }
168
+ /**
169
+ * Installs a visual cursor overlay that follows every `mousemove` on each
170
+ * page in the target. Real synthetic motion from Playwright (e.g.
171
+ * `human.click()`, `human.read(..., { withMotion: true })`) is *already*
172
+ * happening — the page just doesn't render a system cursor for it. This
173
+ * helper injects a HumanJS-styled SVG cursor that listens to mousemove
174
+ * events and follows them, making the motion visible in headed demos and
175
+ * screen recordings.
176
+ *
177
+ * Re-runs on every navigation via `addInitScript`, so the overlay survives
178
+ * page reloads. Idempotent — guard flag on `window` prevents double-install.
179
+ *
180
+ * Accepts either a `Page` (overlay applies to that page) or a
181
+ * `BrowserContext` (overlay applies to every page in the context, including
182
+ * pages opened later).
183
+ *
184
+ * @example
185
+ * ```ts
186
+ * const browser = await chromium.launch({ headless: false });
187
+ * const context = await browser.newContext();
188
+ * await installMouseHelper(context);
189
+ * const page = await context.newPage();
190
+ * // human-driven actions are now visible in the page
191
+ * ```
192
+ */
193
+ declare function installMouseHelper(target: BrowserContext | Page, options?: InstallMouseHelperOptions): Promise<void>;
4
194
 
5
195
  /**
6
196
  * How fast the humanized session runs.
@@ -49,6 +239,77 @@ interface Human {
49
239
  * native `locator.click()` is used directly.
50
240
  */
51
241
  click(target: Locator | string): Promise<void>;
242
+ /**
243
+ * Type `value` into `target` with humanized per-key timing, optional typo
244
+ * injection (with backspace recovery), and occasional think-pauses.
245
+ *
246
+ * Per-key `keydown`/`press`/`up` events fire for each character, so
247
+ * handlers like autocomplete dropdowns still receive every keystroke.
248
+ *
249
+ * In `speed: 'instant'`, falls back to `locator.pressSequentially` with
250
+ * zero inter-key delay — events still fire, but humanization is skipped.
251
+ */
252
+ type(target: Locator | string, value: string): Promise<void>;
253
+ /**
254
+ * Dwell as if reading `target` — the third pillar of humanization after
255
+ * the cursor and the keyboard. Real users pause to read; HumanJS models
256
+ * that pause from word count + the personality's reading WPM (+ jitter).
257
+ *
258
+ * **Targets:**
259
+ * - `string`: a Playwright-compatible selector (matches `click()`/`type()`).
260
+ * - `Locator`: a pre-built Locator.
261
+ * - `{ text }`: literal text in hand (no DOM lookup).
262
+ * - `{ words }`: pre-counted — skip text extraction entirely.
263
+ *
264
+ * **Smart defaults** (only when the caller doesn't override):
265
+ * - `kind` auto-detects as `'code'` for `<pre>` and `<code>` tags;
266
+ * everything else falls back to `'prose'`. Explicit `kind` always wins.
267
+ * - `scrollIntoView: false` — most flows already scrolled to the content.
268
+ *
269
+ * Plugins observe `'read'` actions with `{ target, words, kind }` in params.
270
+ * The text content itself is never echoed — passwords, tokens, and other
271
+ * sensitive strings stay out of telemetry by default.
272
+ *
273
+ * In `speed: 'instant'`, dwell collapses to 0 ms but the action still fires
274
+ * so observability stays consistent.
275
+ *
276
+ * Returns a {@link ReadResult} with the word count, final kind (after
277
+ * auto-detection), and total dwell duration — useful for assertions in
278
+ * tests or for surfacing reading metadata to a UI.
279
+ */
280
+ read(target: ReadTarget, options?: ReadOptions): Promise<ReadResult>;
281
+ /**
282
+ * Scroll the page (or a scrollable container) humanly. Multi-segment
283
+ * motion with a bell-curve velocity profile, optional mid-scroll
284
+ * micro-pauses, and (for the `distracted` personality) the occasional
285
+ * overshoot + correction.
286
+ *
287
+ * **Targets:**
288
+ * - `'natural'` (default): scroll one viewport in the chosen axis
289
+ * - `'end'` / `'top'`: jump to the document/container edges, humanized
290
+ * - `string`: a Playwright-compatible selector — scroll until in view
291
+ * - `Locator`: same, but with a pre-built handle
292
+ * - `{ by: n }`: relative pixel delta (negative = up / left)
293
+ * - `{ to: n }`: absolute scroll position on the chosen axis
294
+ *
295
+ * **Options:** `axis: 'x' | 'y'` (default `'y'`) picks the direction;
296
+ * `within: selector | Locator` scopes the scroll to a scrollable
297
+ * container; `block: 'start' | 'center' | 'end' | 'nearest'` aligns
298
+ * element targets; `overshoot` / `withPauses` toggle individual
299
+ * humanization signals.
300
+ *
301
+ * Plugins observe `'scroll'` actions with `{ target }` in
302
+ * `beforeAction`'s params (a human-readable description of the target).
303
+ * The full {@link ScrollResult} (`from` / `to` / `distance` /
304
+ * `durationMs`) is available via `afterAction`'s `result` argument.
305
+ *
306
+ * In `speed: 'instant'`, the page (or container) is moved with a single
307
+ * `scrollTo` call. No wheel events, no segments — but the action still
308
+ * fires for observability.
309
+ *
310
+ * Returns a {@link ScrollResult} for assertions in tests.
311
+ */
312
+ scroll(target?: ScrollTarget, options?: ScrollOptions): Promise<ScrollResult>;
52
313
  }
53
314
  /**
54
315
  * Creates a humanized session bound to a Playwright `Page`.
@@ -71,4 +332,4 @@ interface Human {
71
332
  */
72
333
  declare function createHuman(page: Page, options?: CreateHumanOptions): Promise<Human>;
73
334
 
74
- export { type CreateHumanOptions, type Human, type Speed, createHuman };
335
+ export { type CreateHumanOptions, type Human, type InstallMouseHelperOptions, type ReadOptions, type ReadResult, type ReadTarget, type ScrollOptions, type ScrollResult, type ScrollTarget, type Speed, createHuman, installMouseHelper };