# How ego-lite's Pointer Driver Handles Clicks, Hovers, Drags, and Wheel Events

> Explore how ego-lite's pointer driver converts semantic actions like click hover drag and wheel into precise Chrome DevTools Protocol mouse events for your embedded browser.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: deep-dive
- Published: 2026-08-01

---

**ego-lite's pointer driver translates high-level interaction helpers into Chrome DevTools Protocol (CDP) mouse events**, converting semantic actions like `click`, `hover`, `drag`, and `wheel` into precise `Input.dispatchMouseEvent` commands for the embedded browser.

The driver lives in [`package/ego-browser/src/driver/pointer.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/driver/pointer.ts) and serves as the low-level bridge between **ego-lite**'s user-facing API and the actual browser runtime. Understanding its implementation reveals how the automation layer achieves reliable, CDP-native pointer control.

## Pointer Driver Architecture

The pointer driver follows a consistent three-phase pattern across all operations:

1. **Element resolution** – selectors, XPath expressions, or `@ref` handles are resolved to concrete DOM node IDs via [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts)
2. **Coordinate calculation** – the target element's center is computed, or explicit `[x, y]` coordinates are used
3. **CDP event dispatch** – `Input.dispatchMouseEvent` payloads are constructed and sent via `ego.sendCDPMessage` defined in [`src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/browser-runtime.ts)

Transient resolution failures surface as `ElementResolutionError` (marked transient) for automatic retry, while permanent failures throw immediately.

## Click and Double-Click Handling

The `click(selector, options)` method implements the standard mouse press lifecycle.

```typescript
// From src/driver/pointer.ts
click: async (selector, options = {}) => {
  const { x, y } = await resolveAndCenter(selector);
  await pointer.down({ x, y, button: options.button ?? 'left' });
  await pointer.up({ x, y, button: options.button ?? 'left' });
}

```

Under the hood, this generates:
- `Input.dispatchMouseEvent` with `type: 'mousePressed'`
- `Input.dispatchMouseEvent` with `type: 'mouseReleased'`

**Double-click** (`dblclick`) performs two sequential click calls with a short delay, setting the `clickCount: 2` parameter in the CDP payload.

```typescript
await dblclick('#icon');  // Resolves to two press/release pairs

```

## Hover (Mouse Movement)

The `hover(selectorOrCoords, options)` method moves the virtual pointer without pressing any buttons.

```typescript
await hover('nav > a.home');     // Element-based hover
await hover([100, 200]);         // Absolute coordinate hover

```

This dispatches a single `type: 'mouseMoved'` event. When an element selector is provided, the driver calculates the bounding box center; raw coordinates bypass resolution entirely.

## Drag-and-Drop Support

The `drag([source, target], options)` method implements the full drag lifecycle as implemented in [`src/driver/pointer.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/pointer.ts):

```typescript
await drag(['#item-1', '#target-area'], {
  steps: 10,  // Number of intermediate move events
});

```

The sequence preserves the same `pointerId` and `pointerType` across:

1. `pointer.down` on source coordinates → `mousePressed`
2. `steps` × `pointer.move` invocations → multiple `mouseMoved` events
3. `pointer.up` on target coordinates → `mouseReleased`

Intermediate steps enable smooth motion tracking, which some JavaScript drag implementations require for proper collision detection.

## Wheel (Scroll) Events

The `wheel(selectorOrCoords, { deltaX, deltaY })` method dispatches scroll events directly:

```typescript
await wheel('#scrollable', { deltaY: 200 });   // Scroll down 200px
await wheel([500, 300], { deltaX: -100 });     // Horizontal scroll at coordinates

```

Unlike click or drag, wheel bypasses the down/up lifecycle and sends `type: 'mouseWheel'` with `deltaX` and `deltaY` values in pixels.

## Low-Level Pointer API

The driver exposes raw pointer methods through `helpers.pointer()`:

```typescript
const ptr = await pointer();

await ptr.move([100, 200]);      // Absolute positioning
await ptr.down({ button: 'right' });  // Right-click press
await ptr.up({ button: 'right' });
await ptr.wheel({ deltaY: 500 });

```

These primitives power the higher-level helpers and enable custom gesture sequences not covered by the standard API.

## Public Helper Integration

The helper façade in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) forwards calls to the driver:

```typescript
// From src/helpers.ts
click: (selector, options = {}) => pointer.click(selector, options),
hover: (target, options = {}) => pointer.hover(target, options),
drag: (targets, options = {}) => pointer.drag(targets, options),
wheel: (target, options) => pointer.wheel(target, options),

```

This two-layer design keeps the public surface minimal while maintaining full driver access for advanced use cases.

## Error Handling and Retries

Resolution failures return structured errors:

- **Transient**: Element not yet in DOM, stale reference → automatic retry by caller
- **Permanent**: Invalid selector, element removed → immediate exception

The `ElementResolutionError` type in [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts) carries a `transient` boolean flag consumed by retry logic in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts).

## Summary

- **Event translation**: All pointer actions map to `Input.dispatchMouseEvent` types: `mousePressed`, `mouseReleased`, `mouseMoved`, `mouseWheel`
- **Source location**: Core implementation in [`package/ego-browser/src/driver/pointer.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/driver/pointer.ts)
- **Resolution layer**: [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts) handles selector-to-DOM-node mapping
- **Transport layer**: [`src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/browser-runtime.ts) provides `ego.sendCDPMessage` for CDP communication
- **Test coverage**: `src/driver/pointer.test.mjs` validates click, hover, drag, and wheel behavior

## Frequently Asked Questions

### What CDP commands does ego-lite's pointer driver use?

The driver exclusively uses `Input.dispatchMouseEvent` with `type` values of `mousePressed`, `mouseReleased`, `mouseMoved`, and `mouseWheel`. These are sent through `ego.sendCDPMessage` as defined in [`src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/browser-runtime.ts).

### How does ego-lite handle element resolution before clicking?

The driver calls the shared element resolver at [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts), which converts CSS selectors, XPath expressions, ARIA roles, or `@ref` handles into concrete Chrome DevTools Protocol node IDs. The resolved element's center coordinates are then used for the pointer action.

### Can I perform drag operations with custom intermediate steps?

Yes. The `drag` method accepts a `steps` option in its options parameter. Higher step counts generate more `mouseMoved` events between source and target, enabling smooth motion that satisfies JavaScript drag-and-drop implementations requiring collision detection during movement.

### What's the difference between `wheel` and scroll-via-drag in ego-lite?

`wheel` dispatches `mouseWheel` CDP events with `deltaX`/`deltaY` values, directly triggering scroll handlers without mouse button states. Drag-based scrolling requires `pointer.down`, `pointer.move` sequences, and `pointer.up`—simulating a user clicking and dragging a scrollbar or touch area.

### Where are pointer events tested in the ego-lite codebase?

The test suite at `package/ego-browser/src/driver/pointer.test.mjs` covers click, double-click, hover, drag, and wheel functionality, validating both successful event dispatch and proper error handling for resolution failures.