From 7d395f39e55fcf267fc5d2a8eeacba69ead9ad46 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Tue, 16 Jun 2026 20:57:44 +0200 Subject: [PATCH] feat(browser): cdp_click_ref/cdp_hover_ref usan cdp_wait_actionable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Antes de calcular el centro y despachar el pointer, ambos esperan a que el elemento sea accionable (visible + stable + hit-test contra elementFromPoint), evitando clicks/hover tragados por overlays/banners o por elementos aún montándose o animándose. Si la comprobación no converge en 2s, se cae al cálculo de centro previo (sin regresión). Modo 'instant' sigue saltando al click JS directo. Co-Authored-By: Claude Opus 4.8 (1M context) --- functions/browser/cdp_click_ref.go | 18 +++++++++++++++++- functions/browser/cdp_click_ref.md | 2 +- functions/browser/cdp_hover_ref.go | 4 ++++ functions/browser/cdp_hover_ref.md | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/functions/browser/cdp_click_ref.go b/functions/browser/cdp_click_ref.go index 2597bafb..2e100618 100644 --- a/functions/browser/cdp_click_ref.go +++ b/functions/browser/cdp_click_ref.go @@ -1,6 +1,15 @@ package browser -import "fmt" +import ( + "fmt" + "time" +) + +// refActionableTimeout es cuánto espera CdpClickRef/CdpHoverRef a que el elemento +// sea accionable (visible+stable+hit-test) antes de caer al cálculo de centro +// previo. Lo bastante para tragar animaciones/overlays transitorios sin penalizar +// el caso común (que converge en ~1 frame). +const refActionableTimeout = 2 * time.Second // refBoxCenter resuelve el centro (x,y) en coords de página de un nodo DOM por su // backendDOMNodeId, vía DOM.getBoxModel. El content quad son 8 floats (4 esquinas). @@ -37,6 +46,13 @@ func CdpClickRef(c *CDPConn, backendNodeID int, opts MouseHumanOpts) error { if opts.Mode == "instant" { return clickRefViaJS(c, backendNodeID) } + // Preferir el punto validado por actionability (visible + stable + hit-test): + // evita clicks tragados por overlays/banners y elementos aún montándose o + // animándose. Si no converge dentro del timeout, se cae al cálculo de centro + // previo (sin regresión). + if x, y, err := CdpWaitActionable(c, backendNodeID, false, refActionableTimeout); err == nil { + return CdpClickXYHuman(c, x, y, opts) + } // scroll al elemento si no está visible; ignorar error (no fatal) _, _ = c.sendCDP("DOM.scrollIntoViewIfNeeded", map[string]any{"backendNodeId": backendNodeID}) cx, cy, err := refBoxCenter(c, backendNodeID) diff --git a/functions/browser/cdp_click_ref.md b/functions/browser/cdp_click_ref.md index 3f1da0ed..bc6e314f 100644 --- a/functions/browser/cdp_click_ref.md +++ b/functions/browser/cdp_click_ref.md @@ -8,7 +8,7 @@ purity: impure signature: "func CdpClickRef(c *CDPConn, backendNodeID int, opts MouseHumanOpts) error" description: "Click humanizado (Bézier + jitter) sobre el elemento identificado por su #ref del AX outline. El #ref es el backendDOMNodeId estable del nodo DOM. Hace scroll al elemento si no está en viewport antes de calcular las coordenadas vía DOM.getBoxModel." tags: [cdp, browser, action, ref, humanized, navegator] -uses_functions: [cdp_click_xy_human_go_browser] +uses_functions: [cdp_click_xy_human_go_browser, cdp_wait_actionable_go_browser] uses_types: [] returns: [] returns_optional: false diff --git a/functions/browser/cdp_hover_ref.go b/functions/browser/cdp_hover_ref.go index 2e28dcd0..ccfd82a1 100644 --- a/functions/browser/cdp_hover_ref.go +++ b/functions/browser/cdp_hover_ref.go @@ -9,6 +9,10 @@ func CdpHoverRef(c *CDPConn, backendNodeID int, opts MouseHumanOpts) error { if c == nil { return fmt.Errorf("cdp hover ref: conexión nil") } + // Preferir el punto validado por actionability; si no converge, caer al centro. + if x, y, err := CdpWaitActionable(c, backendNodeID, false, refActionableTimeout); err == nil { + return CdpMoveMouseHuman(c, x, y, opts) + } // scroll al elemento si no está visible; ignorar error (no fatal) _, _ = c.sendCDP("DOM.scrollIntoViewIfNeeded", map[string]any{"backendNodeId": backendNodeID}) cx, cy, err := refBoxCenter(c, backendNodeID) diff --git a/functions/browser/cdp_hover_ref.md b/functions/browser/cdp_hover_ref.md index e57f74d9..65f33584 100644 --- a/functions/browser/cdp_hover_ref.md +++ b/functions/browser/cdp_hover_ref.md @@ -8,7 +8,7 @@ purity: impure signature: "func CdpHoverRef(c *CDPConn, backendNodeID int, opts MouseHumanOpts) error" description: "Mueve el ratón con trayectoria humanizada (Bézier) sobre el elemento identificado por su #ref del AX outline. Útil para activar menús desplegables, tooltips y cualquier interacción que dependa de hover. El #ref es el backendDOMNodeId estable del nodo DOM." tags: [cdp, browser, action, ref, humanized, navegator] -uses_functions: [cdp_move_mouse_human_go_browser] +uses_functions: [cdp_move_mouse_human_go_browser, cdp_wait_actionable_go_browser] uses_types: [] returns: [] returns_optional: false