npm package: @hieutran094/ghost-cursor-playwright
Description: Move your mouse like a human in playwright or generate realistic movements on any 2D plane
Author: reaz
Latest version: 2.1.6

# ghost-cursor-playwright

Modification of actual ghost-cursor for puppeteer, with more functionality and rewrite to work well with playwright.
Note! target elements rendered in the DOM will be scrolled vertically and horizontally to be visible in viewport

## ✨ Tính năng mới: Timeout cho Mouse Movement

Script sẽ tiếp tục di chuyển chuột đến vị trí cuối cùng và bắn ra exception nếu quá thời gian timeout.

### Download

```shell
git clone https://github.com/reaz1995/ghost-cursor-playwright.git cursor
cd cursor
npm install
```

example of usage in src/example.ts
to run example use

```shell
npm run example
```

for wsl2 don't forget to set up display

### Download as package

```shell
npm i ghost-cursor-playwright
```

```typescript
import { createCursor } from 'ghost-cursor-playwright';
or;
const { createCursor } = require('ghost-cursor-playwright');
```

### How to use

Create and attach cursor to page

```typescript
// Tạo cursor với timeout mặc định (5 giây)
const cursor = await createCursor(page);

// Hoặc tạo cursor với timeout tùy chỉnh
const cursor = await createCursor(page, { timeout: 3000 }); // 3 giây
```

manipulate the cursor via:

```typescript
cursor.actions.move(target: string | BoundingBox | Vector, moveOptions?: moveOptions): Promise<void>;

type moveOptions = {
	paddingPercentage?: number;
	waitForSelector?: number;
	waitBeforeMove?: [number, number];
};


cursor.actions.click(clickOptions?: clickOptions, moveOptions?: moveOptions): Promise<void>;

type clickOptions = {
	target?: string | BoundingBox | Vector;
	waitBeforeClick?: [number, number];
	waitBetweenClick?: [number, number];
	doubleClick?: boolean;
};
// if target is given then cursor will use move function before click
// if target is JS path string, then function will check if for sure is on correct target (sometimes rendered objects are covered in viewport by menu bar or dialogs etc.), if false will proceed fallback to native click


```

util functions

```typescript
// actual position of cursor is mounted on window.mousePos, its value can be retrieve by
  cursor.getActualPosOfMouse(): Promise<Vector>;

// actual target of cursor is mounted on window.mouseTarget, to compare target under cursor with given JS PATH selector use:
  cursor.compareTargetOfMouse(selector: string): Promise<boolean>

// random Vector of element
  cursor.getElemBoundingBox(selector: string): Promise<BoundingBox>
  cursor.getRandomPointInsideElem(elemBoundingBox: BoundingBox, paddingPercentage?): Vector

// random Vector of viewport
  cursor.getRandomPointOnViewport(paddingPercentage?): Promise<Vector>
```

## ⏱️ Timeout và Error Handling

### Cách hoạt động:

1. **Timeout mặc định**: 5 giây cho toàn bộ quá trình di chuyển chuột (không phải từng điểm)
2. **Tiếp tục di chuyển**: Script sẽ tiếp tục di chuyển đến vị trí cuối cùng ngay cả khi chuột thật di chuyển
3. **Exception rõ ràng**: Nếu quá timeout, script sẽ bắn ra exception với thông báo rõ ràng
4. **Timeout thông minh**: Click có timeout riêng (tối đa 1 giây) để không bị chậm

### Ví dụ sử dụng:

```typescript
import { createCursor, GhostCursorTimeoutError, GhostCursorMovementError, GhostCursorClickError } from 'ghost-cursor-playwright';

try {
  const cursor = await createCursor(page, { timeout: 3000 }); // 3 giây timeout
  await cursor.actions.move({ x: 100, y: 100 });
  await cursor.actions.click();
} catch (error) {
  if (error instanceof GhostCursorTimeoutError) {
    console.log('⏰ Timeout Error:', error.message);
    console.log('Operation:', error.operation); // 'move', 'click', 'mouseDown', 'mouseUp'
  } else if (error instanceof GhostCursorMovementError) {
    console.log('🖱️ Movement Error:', error.message);
  } else if (error instanceof GhostCursorClickError) {
    console.log('👆 Click Error:', error.message);
  }
}
```

### Custom Exception Types:

- **`GhostCursorTimeoutError`**: Khi operation bị timeout
  - `operation`: 'move' | 'click' | 'mouseDown' | 'mouseUp'
- **`GhostCursorMovementError`**: Khi di chuyển chuột thất bại
  - `operation`: 'move' | 'click'
- **`GhostCursorClickError`**: Khi click thất bại
  - `operation`: 'click' | 'mouseDown' | 'mouseUp'

### Reset State:

```typescript
// Reset cursor state sau khi bị lỗi
cursor.resetState();

// Ví dụ sử dụng trong error handling
try {
  await cursor.actions.move({ x: 100, y: 100 });
} catch (error) {
  if (error instanceof GhostCursorTimeoutError) {
    console.log('Bị timeout, reset state...');
    cursor.resetState();
    // Thử lại hoặc xử lý khác
  }
}
```

### Lợi ích:

- ✅ Script không bị treo khi chuột thật di chuyển
- ✅ Tiếp tục di chuyển đến vị trí cuối cùng
- ✅ Báo lỗi rõ ràng khi quá timeout
- ✅ Có thể tùy chỉnh thời gian timeout