Files
fn_registry/functions/browser/cdp_click.go
T
egutierrez 5b10b419a2 feat(browser): auto-commit con 44 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-06 12:49:54 +02:00

99 lines
3.1 KiB
Go

package browser
import (
"fmt"
"strconv"
"strings"
)
// CdpClick hace click en el primer elemento que coincide con el selector CSS.
// Obtiene las coordenadas del elemento via Runtime.evaluate y luego despacha
// eventos mousedown, mouseup y click via Input.dispatchMouseEvent.
func CdpClick(c *CDPConn, selector string) error {
if c == nil {
return fmt.Errorf("cdp click: conexion nula")
}
// Obtener coordenadas del centro del elemento, tras hacer scroll para que sea
// visible. Verificamos visibilidad: un elemento existente pero oculto
// (display:none, visibility:hidden, opacity 0 o tamaño 0) daria un rect en
// (0,0) y clicariamos en la esquina sin efecto — devolvemos error en su lugar.
js := fmt.Sprintf(`(function() {
var el = document.querySelector(%q);
if (!el) return null;
el.scrollIntoView({block:'center'});
var r = el.getBoundingClientRect();
var s = window.getComputedStyle(el);
var visible = r.width > 0 && r.height > 0 &&
s.visibility !== 'hidden' && s.display !== 'none' && s.opacity !== '0';
if (!visible) return '__HIDDEN__';
return JSON.stringify({x: r.left + r.width/2, y: r.top + r.height/2});
})()`, selector)
coordStr, err := CdpEvaluate(c, js)
if err != nil {
return fmt.Errorf("cdp click: obtener coordenadas de %q: %w", selector, err)
}
if coordStr == "" || coordStr == "null" {
return fmt.Errorf("cdp click: elemento %q no encontrado en el DOM", selector)
}
if strings.Contains(coordStr, "__HIDDEN__") {
return fmt.Errorf("cdp click: elemento %q existe pero no es visible/clickable (display:none, oculto, opacity 0 o tamaño 0)", selector)
}
// Parsear "{x:...,y:...}" — CdpEvaluate ya retorna el JSON como string
coordStr = strings.Trim(coordStr, `"`)
x, y, err := parseCoords(coordStr)
if err != nil {
return fmt.Errorf("cdp click: parsear coordenadas %q: %w", coordStr, err)
}
// Despachar mousedown
mouseParams := map[string]any{
"type": "mousePressed",
"x": x,
"y": y,
"button": "left",
"clickCount": 1,
}
if _, err := c.sendCDP("Input.dispatchMouseEvent", mouseParams); err != nil {
return fmt.Errorf("cdp click: mousedown: %w", err)
}
// Despachar mouseup
mouseParams["type"] = "mouseReleased"
if _, err := c.sendCDP("Input.dispatchMouseEvent", mouseParams); err != nil {
return fmt.Errorf("cdp click: mouseup: %w", err)
}
return nil
}
// parseCoords extrae x e y de un string JSON como {"x":100,"y":200}.
func parseCoords(s string) (float64, float64, error) {
// Buscar valores x e y manualmente para evitar dependencia de encoding/json
s = strings.TrimSpace(s)
s = strings.TrimPrefix(s, "{")
s = strings.TrimSuffix(s, "}")
var x, y float64
for part := range strings.SplitSeq(s, ",") {
kv := strings.SplitN(strings.TrimSpace(part), ":", 2)
if len(kv) != 2 {
continue
}
k := strings.Trim(strings.TrimSpace(kv[0]), `"`)
v, err := strconv.ParseFloat(strings.TrimSpace(kv[1]), 64)
if err != nil {
return 0, 0, fmt.Errorf("parsear valor %q: %w", kv[1], err)
}
switch k {
case "x":
x = v
case "y":
y = v
}
}
return x, y, nil
}