Repository navigation
feat: Migrate to google-genai SDK, fix vulnerability, and add examples #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
d7c9439
NOLINEAR: migrate to google-genai SDK and add examples
rishitank 461f079
NOLINEAR: format example files with ruff
rishitank aeba845
NOLINEAR: address CodeRabbit review comments
rishitank 198b23d
NOLINEAR: address remaining CodeRabbit suggestions
rishitank bf073f5
NOLINEAR: address final CodeRabbit suggestions
rishitank File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| # AnimaWatch Examples | ||
|
|
||
| Example scripts demonstrating how to use AnimaWatch programmatically. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| 1. Install AnimaWatch and its dependencies: | ||
| ```bash | ||
| cd ~/github/animawatch | ||
| uv sync | ||
| uv run playwright install chromium | ||
| ``` | ||
|
|
||
| 2. Set up your Gemini API key (free): | ||
| ```bash | ||
| export GEMINI_API_KEY="your-api-key-here" | ||
| ``` | ||
| Get a free key at [Google AI Studio](https://aistudio.google.com/). | ||
|
|
||
| ## Examples | ||
|
|
||
| ### 1. Basic Animation Check | ||
|
|
||
| Records a webpage and analyzes it for animation issues. | ||
|
|
||
| ```bash | ||
| uv run python examples/basic_animation_check.py | ||
| ``` | ||
|
|
||
| **What it does:** | ||
| - Opens a browser and navigates to a URL | ||
| - Records for 3 seconds to capture animations | ||
| - Sends the video to Gemini for analysis | ||
| - Reports any jank, stuttering, or timing issues | ||
|
|
||
| ### 2. Screenshot Analysis | ||
|
|
||
| Takes a screenshot and analyzes it for visual/design issues. | ||
|
|
||
| ```bash | ||
| uv run python examples/screenshot_analysis.py | ||
| ``` | ||
|
|
||
| **What it does:** | ||
| - Captures a full-page screenshot | ||
| - Analyzes layout, colors, and typography | ||
| - Reports issues with recommendations | ||
|
|
||
| ### 3. Accessibility Check | ||
|
|
||
| Checks a webpage for visual accessibility issues. | ||
|
|
||
| ```bash | ||
| uv run python examples/accessibility_check.py | ||
| ``` | ||
|
|
||
| **What it does:** | ||
| - Captures a full-page screenshot | ||
| - Analyzes for WCAG accessibility issues | ||
| - Reports contrast, readability, and touch target problems | ||
| - Provides an overall accessibility rating | ||
|
|
||
| ## Customizing Examples | ||
|
|
||
| Edit the URL in any example's `main()` function: | ||
|
|
||
| ```python | ||
| async def main() -> None: | ||
| url = "https://your-website.com" # Change this | ||
| # ... | ||
| ``` | ||
|
|
||
| ## Using with Ollama (100% Local) | ||
|
|
||
| To run without any API calls: | ||
|
|
||
| ```bash | ||
| # Install and start Ollama | ||
| brew install ollama | ||
| ollama serve | ||
|
|
||
| # Pull a vision model | ||
| ollama pull qwen2.5-vl:7b | ||
|
|
||
| # Configure environment | ||
| export VISION_PROVIDER=ollama | ||
| export OLLAMA_MODEL=qwen2.5-vl:7b | ||
|
|
||
| # Run example | ||
| uv run python examples/screenshot_analysis.py | ||
| ``` | ||
|
|
||
| **Note**: Ollama doesn't support direct video analysis, so `basic_animation_check.py` | ||
| will not work with Ollama. Use screenshot-based examples instead. | ||
|
|
||
| ## Writing Your Own Scripts | ||
|
|
||
| ```python | ||
| import asyncio | ||
| from animawatch.browser import BrowserRecorder | ||
| from animawatch.vision import get_vision_provider | ||
|
|
||
| async def my_analysis(): | ||
| browser = BrowserRecorder() | ||
| vision = get_vision_provider() | ||
|
|
||
| try: | ||
| await browser.start() | ||
|
|
||
| # Record with actions | ||
| video_path = await browser.record_interaction( | ||
| url="https://example.com", | ||
| actions=[ | ||
| {"type": "click", "selector": ".menu-button"}, | ||
| {"type": "wait", "duration": 1.0}, | ||
| ], | ||
| wait_time=2.0, | ||
| ) | ||
|
|
||
| # Analyze | ||
| result = await vision.analyze_video(video_path, "Check for animation issues") | ||
| print(result) | ||
|
|
||
| finally: | ||
| await browser.stop() | ||
|
|
||
| asyncio.run(my_analysis()) | ||
| ``` | ||
|
|
||
| ## Available Actions for Recording | ||
|
|
||
| | Action | Parameters | Description | | ||
| |--------|------------|-------------| | ||
| | `click` | `selector` | Click an element | | ||
| | `type` | `selector`, `text` | Type text into input | | ||
| | `scroll` | `x`, `y` | Scroll by pixels | | ||
| | `hover` | `selector` | Hover over element | | ||
| | `wait` | `duration` | Wait for seconds | | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| #!/usr/bin/env python3 | ||
| """Example: Accessibility Check with AnimaWatch. | ||
|
|
||
| This example demonstrates how to use AnimaWatch to check | ||
| a webpage for visual accessibility issues like color contrast, | ||
| readability, and touch target sizes. | ||
| """ | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| import asyncio | ||
|
|
||
| from animawatch.browser import BrowserRecorder | ||
| from animawatch.config import settings | ||
| from animawatch.vision import get_vision_provider | ||
|
|
||
|
|
||
| async def check_accessibility(url: str) -> str: | ||
| """Check a webpage for visual accessibility issues. | ||
|
|
||
| Args: | ||
| url: The webpage URL to check | ||
|
|
||
| Returns: | ||
| The accessibility analysis result | ||
| """ | ||
| from pathlib import Path | ||
|
|
||
| browser = BrowserRecorder() | ||
| vision = get_vision_provider() | ||
| screenshot_path: Path | None = None | ||
|
|
||
| try: | ||
| await browser.start() | ||
|
|
||
| # Take full-page screenshot | ||
| print(f"📸 Capturing {url}...") | ||
| screenshot_path = await browser.take_screenshot(url, full_page=True) | ||
|
|
||
| # Analyze for accessibility | ||
| print("♿ Checking accessibility...") | ||
| prompt = """You are an accessibility expert reviewing a webpage. | ||
|
|
||
| Check for these visual accessibility issues: | ||
|
|
||
| 1. **Color Contrast** | ||
| - Text/background contrast ratios | ||
| - Low contrast UI elements | ||
|
|
||
| 2. **Text Readability** | ||
| - Font sizes (minimum 16px for body) | ||
| - Line height and spacing | ||
| - Font weight and clarity | ||
|
|
||
| 3. **Touch Targets** | ||
| - Button/link sizes (minimum 44x44px) | ||
| - Spacing between interactive elements | ||
|
|
||
| 4. **Focus Indicators** | ||
| - Visible focus states | ||
| - Skip links | ||
|
|
||
| 5. **Visual Hierarchy** | ||
| - Heading structure clarity | ||
| - Information organization | ||
|
|
||
| 6. **Motion Concerns** | ||
| - Animations that could cause vestibular issues | ||
| - Flashing content | ||
|
|
||
| Rate overall accessibility (A, AA, AAA, or Failing) and provide specific recommendations. | ||
| """ | ||
|
|
||
| analysis = await vision.analyze_image(screenshot_path, prompt) | ||
|
|
||
| return str(analysis) | ||
|
|
||
| finally: | ||
| # Clean up temp screenshot | ||
| if screenshot_path is not None and screenshot_path.exists(): | ||
| screenshot_path.unlink() | ||
| await browser.stop() | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
|
|
||
| async def main() -> None: | ||
| """Run the example.""" | ||
| url = "https://example.com" | ||
|
|
||
| print("=" * 60) | ||
| print("♿ AnimaWatch - Accessibility Check Example") | ||
| print("=" * 60) | ||
| print() | ||
| print(f"URL: {url}") | ||
| print(f"Vision Provider: {settings.vision_provider}") | ||
| print() | ||
|
|
||
| try: | ||
| result = await check_accessibility(url) | ||
| print() | ||
| print("=" * 60) | ||
| print("📊 ACCESSIBILITY REPORT") | ||
| print("=" * 60) | ||
| print(result) | ||
| except Exception as e: | ||
| print(f"❌ Error: {e}") | ||
| raise | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| asyncio.run(main()) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| #!/usr/bin/env python3 | ||
| """Example: Basic Animation Check with AnimaWatch. | ||
|
|
||
| This example demonstrates how to use AnimaWatch programmatically | ||
| to check animations on a webpage without running as an MCP server. | ||
| """ | ||
|
|
||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| import asyncio | ||
| from pathlib import Path | ||
|
|
||
| from animawatch.browser import BrowserRecorder | ||
| from animawatch.config import settings | ||
| from animawatch.vision import get_vision_provider | ||
|
|
||
|
|
||
| async def check_animations(url: str, output_dir: Path | None = None) -> str: | ||
| """Record and analyze animations on a webpage. | ||
|
|
||
| Args: | ||
| url: The webpage URL to analyze | ||
| output_dir: Optional directory to save the recording | ||
|
|
||
| Returns: | ||
| The analysis result from the vision AI | ||
| """ | ||
| # Initialize browser recorder and vision provider | ||
| browser = BrowserRecorder() | ||
| vision = get_vision_provider() | ||
| video_path: Path | None = None | ||
|
|
||
| try: | ||
| # Start the browser | ||
| await browser.start() | ||
|
|
||
| # Record the page (waits 3 seconds for animations) | ||
| print(f"🎬 Recording {url}...") | ||
| video_path = await browser.record_interaction( | ||
| url=url, | ||
| actions=None, # No specific actions, just watch | ||
| wait_time=3.0, | ||
| video_dir=output_dir, | ||
| ) | ||
| print(f"✅ Recording saved: {video_path}") | ||
|
|
||
| # Analyze with vision AI | ||
| print("🔍 Analyzing with vision AI...") | ||
| prompt = """Analyze this video for animation issues: | ||
| - Jank or stuttering | ||
| - Timing problems | ||
| - Visual artifacts | ||
| - Layout shifts | ||
|
|
||
| Report any issues with timestamps and severity.""" | ||
|
|
||
| analysis = await vision.analyze_video(video_path, prompt) | ||
|
|
||
| return str(analysis) | ||
|
|
||
| finally: | ||
| # Clean up temp video if no output dir specified | ||
| if output_dir is None and video_path is not None and video_path.exists(): | ||
| video_path.unlink() | ||
| # Always clean up browser | ||
| await browser.stop() | ||
|
|
||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| async def main() -> None: | ||
| """Run the example.""" | ||
| # Example URL - replace with your own | ||
| url = "https://example.com" | ||
|
|
||
| print("=" * 60) | ||
| print("🎬 AnimaWatch - Basic Animation Check Example") | ||
| print("=" * 60) | ||
| print() | ||
| print(f"URL: {url}") | ||
| print(f"Vision Provider: {settings.vision_provider}") | ||
| print(f"Vision Model: {settings.vision_model}") | ||
| print() | ||
|
|
||
| try: | ||
| result = await check_animations(url) | ||
| print() | ||
| print("=" * 60) | ||
| print("📊 ANALYSIS RESULT") | ||
| print("=" * 60) | ||
| print(result) | ||
| except Exception as e: | ||
| print(f"❌ Error: {e}") | ||
| raise | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| asyncio.run(main()) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.