{"record":{"id":"28d5517cd364c8eb","repo":"xpzouying/xiaohongshu-mcp","slug":"0f-0f-0fx-0f","errorCode":null,"errorMessage":"落点 (%.0f,%.0f) 在视口 %.0fx%.0f 之外","messagePattern":"落点 \\(%\\.0f,%\\.0f\\) 在视口 %\\.0fx%\\.0f 之外","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"humanize/input.go","lineNumber":58,"sourceCode":"\tif err != nil || len(shape.Quads) == 0 {\n\t\treturn pt\n\t}\n\treturn jitterInQuad(pt, shape.Quads[0])\n}\n\nfunc ensurePointInViewport(page *rod.Page, pt proto.Point) error {\n\tres, err := page.Eval(`() => JSON.stringify([window.innerWidth, window.innerHeight])`)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar size []float64\n\tif json.Unmarshal([]byte(res.Value.Str()), &size) != nil || len(size) != 2 {\n\t\treturn nil\n\t}\n\n\tif pt.X < 0 || pt.Y < 0 || pt.X > size[0] || pt.Y > size[1] {\n\t\treturn fmt.Errorf(\"落点 (%.0f,%.0f) 在视口 %.0fx%.0f 之外\", pt.X, pt.Y, size[0], size[1])\n\t}\n\treturn nil\n}\n\n// 不用 document.elementFromPoint：结果不稳定\nfunc ensureClickable(elem *rod.Element, pt proto.Point) error {\n\tif err := ensurePointInViewport(elem.Page(), pt); err != nil {\n\t\treturn err\n\t}\n\n\tres, err := elem.Eval(`() => getComputedStyle(this).visibility`)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tif res.Value.Str() == \"hidden\" {\n\t\treturn errors.New(\"元素当前不可命中\")\n\t}\n\treturn nil","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/xpzouying/xiaohongshu-mcp/blob/332d196854a9eac0d2b8c2c0e3d0cc43139d724c/humanize/input.go#L40-L76","documentation":"ensurePointInViewport reads the viewport size (evaluated via JS into a []float64 of [width, height]) and rejects click coordinates that fall outside it. This error means the humanized click point computed for an element lies beyond the visible viewport, so the click would land on nothing.","triggerScenarios":"ensureClickable or ClickAt computes proto.Point pt and calls ensurePointInViewport; the point is negative or exceeds the evaluated viewport width/height (pt.X < 0 || pt.Y < 0 || pt.X > size[0] || pt.Y > size[1]).","commonSituations":"Element is below the fold (y > viewport height) and the code skipped scrolling; the window was resized or the device-scale emulation changed between measuring and clicking; a stale element position cached before a layout shift; clicking an element inside a scrollable inner container whose coordinates exceed the outer viewport; headless run with a small default window size.","solutions":["Scroll the element into view before clicking (e.g. rod's el.MustScrollIntoView() or a JS scrollIntoView call)","Use rod's built-in click helpers that let the browser compute the point (el.MustClick / proto.InputDispatchMouseEvent at the element's box center) instead of manual coordinates","Update the viewport (page.SetViewport) or resize the window to cover the target point","Re-read the element position immediately before clicking to avoid stale coordinates","If the JSON unmarshal of the size fails the check is skipped silently — verify the evaluation returns a proper [w,h] pair"],"exampleFix":"// before\npage.MustEval(\"() => window.scrollTo(0, 0)\")\nel.ClickAt(proto.Point{X: 3000, Y: 4000}) // 落点在视口外\n// after\nel.MustScrollIntoView()\nbox := el.MustShape().Box()\nel.ClickAt(proto.Point{X: box.X + box.Width/2, Y: box.Y + box.Height/2})","handlingStrategy":"validation","validationCode":"// 点击前校验落点\nfunc pointInViewport(pt proto.Point, w, h float64) bool {\n\treturn pt.X >= 0 && pt.Y >= 0 && pt.X <= w && pt.Y <= h\n}","typeGuard":"func validPoint(pt proto.Point, size []float64) bool {\n\treturn len(size) == 2 && pt.X >= 0 && pt.Y >= 0 && pt.X <= size[0] && pt.Y <= size[1]\n}","tryCatchPattern":"if err := ensurePointInViewport(page, pt); err != nil {\n\t// 落点在视口外：先滚动元素到可视区，再重新计算坐标\n\tel.MustScrollIntoView()\n\tpt = recomputePoint(el)\n\tif err := ensurePointInViewport(page, pt); err != nil {\n\t\treturn fmt.Errorf(\"滚动后落点仍不可用: %w\", err)\n\t}\n}","preventionTips":["Always scroll the element into view before computing/clicking points","Re-read element positions immediately before clicking; never cache across layout changes","Set an explicit viewport size (page.SetViewport) instead of relying on defaults","Account for device pixel ratio / zoom when converting coordinates"],"tags":["browser","viewport","coordinates","rod","click"],"backgroundTag":"click-outside-viewport","analyzedSha":"332d196854a9eac0d2b8c2c0e3d0cc43139d724c","analyzedAt":"2026-09-05T22:22:55.988Z","contentChangedAt":"2026-09-05T22:22:55.988Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}