Skip to content

v0.41.0 — a real mouse pointer

Choose a tag to compare

@shreyaskarnik shreyaskarnik released this 15 Sep 21:10
· 8 commits to main since this release

Demo recordings can now show a real mouse pointer: an SVG arrow that glides to what it's about to click, pauses, and presses. Viewers see where to look before anything happens.

Added

createHumanCursor(): a visible, human-paced pointer (#51)

import { createHumanCursor, cursorHighlight, resetCursor } from '@argo-video/cli';

await cursorHighlight(page, { mode: 'click' });
const cursor = await createHumanCursor(page, { seed: 'my-demo', size: 30 });
await cursor.click(page.getByRole('button', { name: 'Continue' }));
await cursor.moveTo(page.getByRole('link', { name: 'Details' }), { durationMs: 900 });
await cursor.dispose();
await resetCursor(page);
  • Natural, repeatable movement. The pointer follows a slightly irregular curve that eases in and out, and a short pause precedes each click. Paths are seeded, so re-recording a demo gives the same motion, which keeps Argo's shader-transition cache valid.
  • Real mouse events. Every step of the path is a real page.mouse.move(), so hover states, cursorHighlight, and trackCursor telemetry all respond as they would to a person.
  • Clicks go through Playwright. The press itself uses locator.click(), so if the layout shifts while the pointer is travelling (a late image, a toast), Playwright waits or retargets instead of clicking the wrong spot.
  • The pointer doesn't hide the label. It lands inside the control's padding by default. target sets a different point inside the element, and durationMs, dwellMs, holdMs and afterMs tune the timing.
  • Works across a whole demo. It survives page navigation, and one instance can carry through every scene so the pointer never jumps back to the centre. Pair cursor.click(field) with demoType() so typing follows visible travel.

cursorHighlight({ mode: 'click' }): brief locator circles

A new mode for cursorHighlight and video.cursorHighlight. Instead of a ring that constantly follows the mouse, a circle briefly contracts toward the pointer tip in three cases: when the pointer first appears, on each click, and when Control or Meta is released on its own. Chords like Control+A and Meta+C don't trigger it.

In click mode the circles sit above overlays and below the pointer. The default mode: 'continuous' is unchanged: the existing ring and click ripple keep their old layers beneath overlays, so recordings made before this release render the same.

A self-contained demo is included at demos/pseudo-cursor.demo.ts. It needs no app server, TTS engine or API keys: watch it.

Contributors

Thank you to @OleksandrKucherenko for #51. It started as a working prototype posted in #37, with a recorded video showing the idea, and grew into a finished feature. Every review point was addressed with a test written before its fix. I reverted the fixes one at a time and each revert failed a test, which is exactly what you want from a test suite. #37 was filed by @salir-admin, who pointed out that a recording with no visible pointer leaves viewers unsure where to look.

The path generation adapts ideas from CloverLabsAI/human-cursor (MIT), and moving via real mouse events follows feder-cr/invisible_playwright (MIT). Both are credited in the source.

Verified

891 tests on Node 20 and 24, Chromium/Firefox/WebKit smoke, and an end-to-end run of the packed tarball. npm audit clean.

Full changelog: v0.40.0...v0.41.0