package browser import ( "fmt" "math/rand" "time" ) // CdpClickXYHuman hace click en las coordenadas absolutas (x, y) de la página con // comportamiento humano: mueve el ratón hasta el punto por una trayectoria de // Bézier cúbica (CdpMoveMouseHuman) y despacha mousePressed/mouseReleased con una // micro-pausa variable (30-90 ms) entre ambos. // // Es el PRIMITIVO de click compartido por las tres vías de acción del agente: // - por selector CSS → CdpClickHuman (obtiene el bbox y llama aquí). // - por #ref del AX tree → CdpClickRef (resuelve backendDOMNodeId → bbox → aquí). // - por visión → click sobre el bounding box que devuelve OCR/YOLO. // Construir un único primitivo evita tener tres caminos de click divergentes. func CdpClickXYHuman(c *CDPConn, x, y float64, opts MouseHumanOpts) error { if c == nil { return fmt.Errorf("cdp click xy human: conexion nula") } // Mover el ratón hasta el destino con trayectoria humana. if err := CdpMoveMouseHuman(c, x, y, opts); err != nil { return fmt.Errorf("cdp click xy human: mover raton: %w", err) } clickParams := map[string]any{ "type": "mousePressed", "x": x, "y": y, "button": "left", "clickCount": 1, } if _, err := c.sendCDP("Input.dispatchMouseEvent", clickParams); err != nil { return fmt.Errorf("cdp click xy human: mousePressed: %w", err) } // Micro-pausa humana entre press y release (30-90 ms). time.Sleep(time.Duration(30+rand.Intn(61)) * time.Millisecond) clickParams["type"] = "mouseReleased" if _, err := c.sendCDP("Input.dispatchMouseEvent", clickParams); err != nil { return fmt.Errorf("cdp click xy human: mouseReleased: %w", err) } return nil }