feat(browser): auto-commit con 60 cambios

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-07 11:42:31 +02:00
parent 37aacfcfa9
commit 8742cb25be
71 changed files with 5660 additions and 192 deletions
+92
View File
@@ -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`.