feat(browser): auto-commit con 60 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
---
|
||||
name: cdp_click_xy
|
||||
kind: function
|
||||
lang: py
|
||||
domain: browser
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def cdp_click_xy(x: int, y: int, *, port: int = 9222, target_url_substr: str = '', move_first: bool = True, timeout_s: float = 10.0) -> dict"
|
||||
description: "Hace un click izquierdo de raton REAL en coordenadas (x, y) del viewport de una pestana de un Chrome con remote debugging, via CDP Input.dispatchMouseEvent (mouseMoved opcional + mousePressed + mouseReleased). Primitiva de input CDP reutilizable: necesaria cuando element.click() de JavaScript NO dispara los handlers de React de SPAs (WhatsApp Web): abrir un chat de la lista o un resultado de busqueda requiere un click de raton sintetico real. El caller resuelve las coordenadas con cdp_eval (getBoundingClientRect -> centro) y las pasa aqui."
|
||||
tags: [cdp, browser, automation, python, navegator]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: ["json", "time", "urllib.request", "websocket"]
|
||||
params_schema:
|
||||
params:
|
||||
- name: x
|
||||
desc: "Coordenada X en CSS px del viewport donde hacer click. Normalmente el centro del elemento (getBoundingClientRect via cdp_eval)."
|
||||
- name: y
|
||||
desc: "Coordenada Y en CSS px del viewport donde hacer click. Normalmente el centro del elemento (getBoundingClientRect via cdp_eval)."
|
||||
- name: port
|
||||
desc: "Puerto de remote debugging de Chrome. Default 9222."
|
||||
- name: target_url_substr
|
||||
desc: "Substring que debe contener la URL del target (pestana). Si vacio, usa el primer target de tipo 'page'."
|
||||
- name: move_first
|
||||
desc: "Si True, emite un mouseMoved a (x, y) antes del click para que la SPA registre el hover. Default True."
|
||||
- name: timeout_s
|
||||
desc: "Timeout en segundos para la conexion WebSocket. Default 10.0."
|
||||
output: "dict {ok: bool, error: str, x: int, y: int}. ok=True si los eventos de raton (mouseMoved opcional, mousePressed, mouseReleased) se emitieron sin error; x/y son eco de los argumentos. Nunca lanza: errores de red/conexion/transport se devuelven en 'error' con ok=False."
|
||||
tested: true
|
||||
tests: ["test_golden_click_emite_movido_pressed_released_left", "test_edge_move_first_false_omite_mousemoved", "test_error_create_connection_lanza_ok_false"]
|
||||
test_file_path: "python/functions/browser/cdp_click_xy_test.py"
|
||||
file_path: "python/functions/browser/cdp_click_xy.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
import sys, os, json
|
||||
sys.path.insert(0, os.path.join("python", "functions"))
|
||||
from browser.cdp_eval import cdp_eval
|
||||
from browser.cdp_click_xy import cdp_click_xy
|
||||
|
||||
# Requiere un Chrome lanzado con --remote-debugging-port=9222
|
||||
# y una pestana de WhatsApp Web abierta.
|
||||
# Localizar el row de un chat por nombre exacto y abrirlo con click REAL.
|
||||
r = cdp_eval(r'''(() => {
|
||||
const row = [...document.querySelectorAll('#side [role="row"]')]
|
||||
.find(x => /^NOTAS WASAP\b/.test(x.innerText.replace(/\s+/g,' ').trim()));
|
||||
if(!row) return null;
|
||||
const b = row.getBoundingClientRect();
|
||||
return JSON.stringify({x: Math.round(b.x+b.width/2), y: Math.round(b.y+b.height/2)});
|
||||
})()''', target_url_substr="whatsapp")
|
||||
|
||||
c = json.loads(r["value"])
|
||||
res = cdp_click_xy(c["x"], c["y"], target_url_substr="whatsapp") # abre el chat
|
||||
print(res["ok"], res["error"])
|
||||
```
|
||||
|
||||
O directo por CLI: `python3 python/functions/browser/cdp_click_xy.py 100 200 "whatsapp"`.
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Cuando necesites **clickar un elemento** y `element.click()` de JavaScript NO dispara
|
||||
sus handlers (SPAs React como WhatsApp Web): **abrir un chat de la lista**, abrir un
|
||||
**resultado de busqueda**, pulsar un boton que React renderiza con listeners propios.
|
||||
Resuelve primero las coordenadas del elemento con `cdp_eval_py_browser`
|
||||
(`getBoundingClientRect` -> centro) y pasa `x, y` aqui. Es la primitiva de input de
|
||||
raton sobre la que se construyen funciones `whatsapp_*_py_browser` y cualquier script
|
||||
que opere una pestana existente via CDP. Para teclas usa `cdp_press_key_py_browser`;
|
||||
para escribir texto, `cdp_type_chars_py_browser`.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Coordenadas en CSS px del viewport**: `getBoundingClientRect()` ya las devuelve en
|
||||
ese sistema, por eso encaja directo. No uses `pageX/pageY` ni coords absolutas de
|
||||
documento.
|
||||
- **El elemento debe estar VISIBLE en el viewport**: si esta fuera de pantalla por
|
||||
scroll, las coords del rect son invalidas (negativas o fuera de rango) y el click cae
|
||||
en el lugar equivocado. Haz scroll al elemento primero (via `cdp_eval` con
|
||||
`scrollIntoView`) y vuelve a leer el rect.
|
||||
- Es un **click izquierdo simple** (clickCount=1, button=left). No hace doble click,
|
||||
click derecho ni drag.
|
||||
- `move_first=True` (default) emite un `mouseMoved` previo para que la SPA registre el
|
||||
hover; algunas UIs solo muestran/activan controles tras hover. Ponlo en False si no
|
||||
lo necesitas.
|
||||
- Requiere un Chrome lanzado con `--remote-debugging-port=9222` (o el puerto que pases).
|
||||
Sin remote debugging, `GET /json` falla y devuelve `ok=False`.
|
||||
- Nunca lanza: errores de red, conexion WS o transport se reportan en el campo `error`
|
||||
con `ok=False`.
|
||||
Reference in New Issue
Block a user