"""Trusted input primitives for menu automation. Radix triggers (dock menu, permission-mode choosers) ignore synthetic JS clicks: they need real CDP Input.dispatchMouseEvent press+release. """ import json import time from approvals import _cdp_req_ids as _shared_cdp_ids, cdp_evaluate ESCAPE_JS = ("(() => { document.dispatchEvent(new KeyboardEvent(" "'keydown', {key: 'Escape', code: 'Escape'," " bubbles: true})); return 'ESC'; })()") class MouseError(RuntimeError): """Trusted click failed (CDP transport or echo timeout).""" def escape(ws): """Dismiss topmost popover/menu/dialog. Never raises.""" try: cdp_evaluate(ws, ESCAPE_JS, timeout=3.0) except Exception: pass def close(ws): """Close a CDP websocket. Never raises.""" try: ws.close() except Exception: pass def real_click(ws, x, y, timeout=5.0): """Trusted press+release at page coordinates. Shares approvals' monotonic CDP id counter so ids stay unique on the connection; matches responses by id like cdp_evaluate. Raises MouseError on transport or echo-timeout failure. """ try: for typ in ("mousePressed", "mouseReleased"): req_id = next(_shared_cdp_ids) ws.send(json.dumps({"id": req_id, "method": "Input.dispatchMouseEvent", "params": {"type": typ, "x": x, "y": y, "button": "left", "clickCount": 1}})) deadline = time.time() + timeout while time.time() < deadline: resp = json.loads(ws.recv()) if resp.get("id") == req_id: break else: raise MouseError("mouse echo timeout for %s" % typ) except MouseError: raise except Exception as e: raise MouseError("real click failed: %s: %s" % (type(e).__name__, e))