|
| 1 | +""" |
| 2 | +Visual overlay utilities - show/clear element highlights in browser |
| 3 | +""" |
| 4 | + |
| 5 | +from typing import Any |
| 6 | + |
| 7 | +from .browser import SentienceBrowser |
| 8 | +from .models import Element, Snapshot |
| 9 | + |
| 10 | + |
| 11 | +def show_overlay( |
| 12 | + browser: SentienceBrowser, |
| 13 | + elements: list[Element] | list[dict[str, Any]] | Snapshot, |
| 14 | + target_element_id: int | None = None, |
| 15 | +) -> None: |
| 16 | + """ |
| 17 | + Display visual overlay highlighting elements in the browser |
| 18 | +
|
| 19 | + This function shows a Shadow DOM overlay with color-coded borders around |
| 20 | + detected elements. Useful for debugging, learning, and validating element detection. |
| 21 | +
|
| 22 | + Args: |
| 23 | + browser: SentienceBrowser instance |
| 24 | + elements: Can be: |
| 25 | + - List of Element objects (from snapshot.elements) |
| 26 | + - List of raw element dicts (from snapshot result or API response) |
| 27 | + - Snapshot object (will use snapshot.elements) |
| 28 | + target_element_id: Optional ID of element to highlight in red (default: None) |
| 29 | +
|
| 30 | + Color Coding: |
| 31 | + - Red: Target element (when target_element_id is specified) |
| 32 | + - Blue: Primary elements (is_primary=true) |
| 33 | + - Green: Regular interactive elements |
| 34 | +
|
| 35 | + Visual Indicators: |
| 36 | + - Border thickness and opacity scale with importance score |
| 37 | + - Semi-transparent fill for better visibility |
| 38 | + - Importance badges showing scores |
| 39 | + - Star icon for primary elements |
| 40 | + - Target emoji for the target element |
| 41 | +
|
| 42 | + Auto-clear: Overlay automatically disappears after 5 seconds |
| 43 | +
|
| 44 | + Example: |
| 45 | + # Show overlay from snapshot |
| 46 | + snap = snapshot(browser) |
| 47 | + show_overlay(browser, snap) |
| 48 | +
|
| 49 | + # Show overlay with custom elements |
| 50 | + elements = [{"id": 1, "bbox": {"x": 100, "y": 100, "width": 200, "height": 50}, ...}] |
| 51 | + show_overlay(browser, elements) |
| 52 | +
|
| 53 | + # Show overlay with target element highlighted in red |
| 54 | + show_overlay(browser, snap, target_element_id=42) |
| 55 | +
|
| 56 | + # Clear overlay manually before 5 seconds |
| 57 | + clear_overlay(browser) |
| 58 | + """ |
| 59 | + if not browser.page: |
| 60 | + raise RuntimeError("Browser not started. Call browser.start() first.") |
| 61 | + |
| 62 | + # Handle different input types |
| 63 | + if isinstance(elements, Snapshot): |
| 64 | + # Extract elements from Snapshot object |
| 65 | + elements_list = [el.model_dump() for el in elements.elements] |
| 66 | + elif isinstance(elements, list) and len(elements) > 0: |
| 67 | + # Check if it's a list of Element objects or dicts |
| 68 | + if hasattr(elements[0], "model_dump"): |
| 69 | + # List of Element objects |
| 70 | + elements_list = [el.model_dump() for el in elements] |
| 71 | + else: |
| 72 | + # Already a list of dicts |
| 73 | + elements_list = elements |
| 74 | + else: |
| 75 | + raise ValueError("elements must be a Snapshot, list of Element objects, or list of dicts") |
| 76 | + |
| 77 | + # Call extension API |
| 78 | + browser.page.evaluate( |
| 79 | + """ |
| 80 | + (args) => { |
| 81 | + if (window.sentience && window.sentience.showOverlay) { |
| 82 | + window.sentience.showOverlay(args.elements, args.targetId); |
| 83 | + } else { |
| 84 | + console.warn('[Sentience SDK] showOverlay not available - is extension loaded?'); |
| 85 | + } |
| 86 | + } |
| 87 | + """, |
| 88 | + {"elements": elements_list, "targetId": target_element_id}, |
| 89 | + ) |
| 90 | + |
| 91 | + |
| 92 | +def clear_overlay(browser: SentienceBrowser) -> None: |
| 93 | + """ |
| 94 | + Clear the visual overlay manually (before 5-second auto-clear) |
| 95 | +
|
| 96 | + Args: |
| 97 | + browser: SentienceBrowser instance |
| 98 | +
|
| 99 | + Example: |
| 100 | + show_overlay(browser, snap) |
| 101 | + # ... inspect overlay ... |
| 102 | + clear_overlay(browser) # Remove immediately |
| 103 | + """ |
| 104 | + if not browser.page: |
| 105 | + raise RuntimeError("Browser not started. Call browser.start() first.") |
| 106 | + |
| 107 | + browser.page.evaluate( |
| 108 | + """ |
| 109 | + () => { |
| 110 | + if (window.sentience && window.sentience.clearOverlay) { |
| 111 | + window.sentience.clearOverlay(); |
| 112 | + } |
| 113 | + } |
| 114 | + """ |
| 115 | + ) |
0 commit comments