Scroll the page by specified number of pages (set down=True to scroll down, down=False to scroll up, num_pages=number of pages to scroll like 0.5 for half page, 10.0 for ten pages, etc.). Default behavior is to scroll the entire page. This is enough for most cases. Optional if there
(self, request: ScrollRequest)
| 474 | ) |
| 475 | |
| 476 | async def scroll(self, request: ScrollRequest) -> ActionResult: |
| 477 | """Scroll the page by specified number of pages (set down=True to scroll down, down=False to scroll up, num_pages=number of pages to scroll like 0.5 for half page, 10.0 for ten pages, etc.). |
| 478 | Default behavior is to scroll the entire page. This is enough for most cases. |
| 479 | Optional if there are multiple scroll containers, use frame_element_index parameter with an element inside the container you want to scroll in. For that you must use indices that exist in your browser_state (works well for dropdowns and custom UI components). |
| 480 | Instead of scrolling step after step, use a high number of pages at once like 10 to get to the bottom of the page. |
| 481 | If you know where you want to scroll to, use scroll_to_text instead of this tool. |
| 482 | """ |
| 483 | try: |
| 484 | # Look up the node from the selector map if index is provided |
| 485 | # Special case: index 0 means scroll the whole page (root/body element) |
| 486 | node = None |
| 487 | if request.frame_element_index is not None and request.frame_element_index != 0: |
| 488 | node = await self._session.get_element_by_index(request.frame_element_index) |
| 489 | if node is None: |
| 490 | # Element does not exist |
| 491 | return ActionResult( |
| 492 | success=False, |
| 493 | message=f'Element index {request.frame_element_index} not found in browser state', |
| 494 | extra={"error": f"Element index {request.frame_element_index} not found"} |
| 495 | ) |
| 496 | |
| 497 | # Dispatch scroll event with node - the complex logic is handled in the event handler |
| 498 | # Convert pages to pixels (assuming 1000px per page as standard viewport height) |
| 499 | pixels = int(request.num_pages * 1000) |
| 500 | event = self._session.event_bus.dispatch( |
| 501 | ScrollEvent(direction='down' if request.down else 'up', amount=pixels, node=node) |
| 502 | ) |
| 503 | await event |
| 504 | await event.event_result(raise_if_any=True, raise_if_none=False) |
| 505 | direction = 'down' if request.down else 'up' |
| 506 | |
| 507 | # If index is 0 or None, we're scrolling the page |
| 508 | target = ( |
| 509 | 'the page' |
| 510 | if request.frame_element_index is None or request.frame_element_index == 0 |
| 511 | else f'element {request.frame_element_index}' |
| 512 | ) |
| 513 | |
| 514 | if request.num_pages == 1.0: |
| 515 | long_term_memory = f'Scrolled {direction} {target} by one page' |
| 516 | else: |
| 517 | long_term_memory = f'Scrolled {direction} {target} by {request.num_pages} pages' |
| 518 | |
| 519 | msg = f'🔍 {long_term_memory}' |
| 520 | logger.info(msg) |
| 521 | return ActionResult( |
| 522 | success=True, |
| 523 | message=f"Successfully scrolled {direction} {target}", |
| 524 | extra={"memory": long_term_memory} |
| 525 | ) |
| 526 | except Exception as e: |
| 527 | logger.error(f'Failed to dispatch ScrollEvent: {type(e).__name__}: {e}') |
| 528 | error_msg = 'Failed to execute scroll action.' |
| 529 | return ActionResult( |
| 530 | success=False, |
| 531 | message=error_msg, |
| 532 | extra={"error": str(e)} |
| 533 | ) |
nothing calls this directly
no test coverage detected