Relative Mouse Movement
The mouse wrapper exposes only absolute set_mouse_position — there was no
“nudge the pointer by (dx, dy)” (the pynput / PyAutoGUI moveRel staple),
which relative-pointer / canvas / FPS-style apps and incremental drags need.
relative_target() is the pure arithmetic (current + delta) and is
unit-testable; move_mouse_relative() reads the live position and sets the
new one, with both the getter and setter injectable so it is tested without a
real pointer. Imports no PySide6.
Headless API
from je_auto_control import move_mouse_relative, relative_target
move_mouse_relative(-40, 12) # nudge left 40, down 12 from where it is
# {'from': [200, 200], 'to': [160, 212], 'delta': [-40, 12]}
relative_target((100, 100), 10, -5) # (110, 95) — pure, no I/O
move_mouse_relative reads the current position (raising
AutoControlMouseException if it cannot), adds the delta, and moves there.
get_position / set_position default to the real mouse wrapper but are
injectable for headless tests.
Executor commands
AC_move_mouse_relative takes dx / dy and returns {from, to,
delta}. It is exposed as the MCP tool ac_move_mouse_relative and as a
Script Builder command under Mouse.