Skip to content

Commit 651ea1e

Browse files
abrichrclaude
andcommitted
refactor(viewer): convert to reusable component library
BREAKING CHANGE: Architecture refactored to component-based design. Backward compatible - existing API still works. New structure: - components/ - 8 reusable UI components (screenshot, playback, etc.) - builders/ - PageBuilder fluent API for page construction - styles/ - Shared CSS with oa-* prefix classes - examples/ - 4 reference implementations (benchmark, training, capture, retrieval) Features: - Component functions return HTML strings (composable) - Shared CSS variables for consistent theming - 115 tests passing - Other packages can import individual components Usage: from openadapt_viewer.components import screenshot_display from openadapt_viewer.builders import PageBuilder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent dddb6d0 commit 651ea1e

25 files changed

Lines changed: 4187 additions & 85 deletions

CLAUDE.md

Lines changed: 224 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,77 +2,259 @@
22

33
## Overview
44

5-
HTML visualization generators for training dashboards, benchmark results, and capture playback. Generates standalone HTML files that can be opened in any browser.
5+
**Reusable component library** for OpenAdapt visualization. Provides building blocks (components) and high-level builders for creating standalone HTML viewers.
6+
7+
Used by:
8+
- **openadapt-ml**: Training dashboards
9+
- **openadapt-evals**: Benchmark result viewers
10+
- **openadapt-capture**: Capture playback viewers
11+
- **openadapt-retrieval**: Demo search result viewers
612

713
## Quick Start
814

915
```bash
1016
# Install
17+
cd /Users/abrichr/oa/src/openadapt-viewer
1118
uv sync
1219

20+
# Run tests
21+
uv run pytest tests/ -v
22+
1323
# Generate demo benchmark viewer
1424
uv run openadapt-viewer demo --tasks 5 --output viewer.html
1525

16-
# Open in browser
17-
open viewer.html
26+
# Run examples
27+
uv run python -m openadapt_viewer.examples.benchmark_example
28+
uv run python -m openadapt_viewer.examples.training_example
29+
uv run python -m openadapt_viewer.examples.capture_example
30+
uv run python -m openadapt_viewer.examples.retrieval_example
1831
```
1932

20-
## CLI Commands
21-
22-
| Command | Description |
23-
|---------|-------------|
24-
| `demo` | Generate demo viewer with sample data |
25-
| `benchmark` | Generate viewer from benchmark results |
26-
27-
## Key Files
28-
29-
- `viewers/benchmark/generator.py` - Main HTML generator
30-
- `viewers/benchmark/data.py` - Data models (BenchmarkRun, TaskExecution)
31-
- `viewers/benchmark/templates/` - Jinja2 templates
32-
- `__main__.py` - CLI entry point
33-
3433
## Architecture
3534

3635
```
3736
openadapt_viewer/
38-
├── viewers/
39-
│ └── benchmark/
37+
├── components/ # Reusable UI building blocks
38+
│ ├── screenshot.py # Screenshot with overlays
39+
│ ├── playback.py # Play/pause/speed controls
40+
│ ├── timeline.py # Step progress bar
41+
│ ├── action_display.py # Format actions (click, type, etc.)
42+
│ ├── metrics.py # Stats cards and grids
43+
│ ├── filters.py # Filter dropdowns
44+
│ ├── list_view.py # Selectable list component
45+
│ └── badge.py # Status badges
46+
47+
├── builders/ # High-level page builders
48+
│ └── page_builder.py # PageBuilder class
49+
50+
├── styles/ # Shared CSS
51+
│ └── core.css # CSS variables and base styles
52+
53+
├── core/ # Core utilities
54+
│ ├── types.py # Pydantic data models
55+
│ ├── data_loader.py # Data loading utilities
56+
│ └── html_builder.py # Jinja2 HTMLBuilder
57+
58+
├── viewers/ # Full viewer implementations
59+
│ └── benchmark/ # Benchmark results viewer
4060
│ ├── generator.py # generate_benchmark_html()
41-
│ ├── data.py # BenchmarkRun, TaskExecution, ExecutionStep
42-
│ └── templates/
43-
│ └── benchmark.html.j2
44-
├── __init__.py
45-
└── __main__.py # CLI
61+
│ └── data.py # Data loading
62+
63+
├── examples/ # Reference implementations
64+
│ ├── benchmark_example.py # openadapt-evals usage
65+
│ ├── training_example.py # openadapt-ml usage
66+
│ ├── capture_example.py # openadapt-capture usage
67+
│ └── retrieval_example.py # openadapt-retrieval usage
68+
69+
├── templates/ # Jinja2 templates
70+
│ └── base.html # Base HTML template
71+
72+
└── __init__.py # Package exports
4673
```
4774

48-
## HTML Features
75+
## Component Usage
4976

50-
The generated viewer includes:
51-
- Summary statistics (pass/fail, success rate)
52-
- Domain breakdown charts
53-
- Task list with filtering
54-
- Step-by-step screenshot playback
55-
- Playback controls (prev/next, play/pause, speed)
56-
- Keyboard shortcuts
77+
### Individual Components
5778

58-
## Integration
79+
Each component returns an HTML string:
80+
81+
```python
82+
from openadapt_viewer.components import (
83+
screenshot_display,
84+
playback_controls,
85+
metrics_grid,
86+
filter_bar,
87+
selectable_list,
88+
badge,
89+
)
90+
91+
# Screenshot with overlays
92+
html = screenshot_display(
93+
image_path="screenshot.png",
94+
overlays=[
95+
{"type": "click", "x": 0.5, "y": 0.3, "label": "H", "variant": "human"},
96+
{"type": "click", "x": 0.6, "y": 0.4, "label": "AI", "variant": "predicted"},
97+
],
98+
caption="Step 5",
99+
)
100+
101+
# Metrics cards
102+
html = metrics_grid([
103+
{"label": "Total", "value": 100},
104+
{"label": "Passed", "value": 75, "color": "success"},
105+
{"label": "Failed", "value": 25, "color": "error"},
106+
])
107+
108+
# Playback controls (requires Alpine.js)
109+
html = playback_controls(step_count=20, initial_step=0)
110+
111+
# Filter bar
112+
html = filter_bar(
113+
filters=[
114+
{"id": "domain", "label": "Domain", "options": ["office", "browser"]},
115+
{"id": "status", "label": "Status", "options": ["passed", "failed"]},
116+
],
117+
search_placeholder="Search tasks...",
118+
)
119+
```
59120

60-
Used by openadapt-ml and openadapt-evals to visualize results:
121+
### Page Builder
122+
123+
Build complete pages from components:
61124

62125
```python
63-
from openadapt_viewer import generate_benchmark_html, BenchmarkRun
126+
from openadapt_viewer.builders import PageBuilder
127+
from openadapt_viewer.components import metrics_grid, screenshot_display
128+
129+
builder = PageBuilder(title="My Viewer", include_alpine=True)
130+
131+
builder.add_header(
132+
title="Results",
133+
subtitle="Model: gpt-5.1",
134+
nav_tabs=[
135+
{"href": "dashboard.html", "label": "Training"},
136+
{"href": "viewer.html", "label": "Viewer", "active": True},
137+
],
138+
)
139+
140+
builder.add_section(
141+
metrics_grid([...]),
142+
title="Summary",
143+
)
144+
145+
builder.add_section(
146+
screenshot_display("screenshot.png"),
147+
)
148+
149+
# Render to string
150+
html = builder.render()
64151

65-
run = BenchmarkRun.from_results(results)
66-
html = generate_benchmark_html(run, embed_screenshots=True)
67-
Path("results.html").write_text(html)
152+
# Or write to file
153+
path = builder.render_to_file("output.html")
68154
```
69155

70-
## Development
156+
### Ready-to-Use Viewers
157+
158+
```python
159+
# Benchmark viewer
160+
from openadapt_viewer.viewers.benchmark import generate_benchmark_html
161+
generate_benchmark_html(data_path="results/", output_path="viewer.html")
162+
163+
# Or with data
164+
from openadapt_viewer import BenchmarkRun, generate_benchmark_html
165+
run = BenchmarkRun(...)
166+
generate_benchmark_html(run_data=run, output_path="viewer.html")
167+
```
168+
169+
## CSS Classes
170+
171+
All component classes use the `oa-` prefix:
172+
173+
| Component | Classes |
174+
|-----------|---------|
175+
| Screenshot | `oa-screenshot-container`, `oa-overlay`, `oa-overlay-click` |
176+
| Playback | `oa-playback-controls`, `oa-playback-btn`, `oa-playback-speed` |
177+
| Timeline | `oa-timeline`, `oa-timeline-track`, `oa-timeline-progress` |
178+
| Metrics | `oa-metrics-card`, `oa-metrics-grid`, `oa-metrics-value` |
179+
| Filters | `oa-filter-bar`, `oa-filter-dropdown` |
180+
| List | `oa-list`, `oa-list-item`, `oa-list-item-selected` |
181+
| Badge | `oa-badge`, `oa-badge-success`, `oa-badge-error` |
182+
183+
## CSS Variables
184+
185+
Core CSS variables in `styles/core.css`:
186+
187+
```css
188+
:root {
189+
--oa-bg-primary: #0a0a0f;
190+
--oa-bg-secondary: #12121a;
191+
--oa-text-primary: #f0f0f0;
192+
--oa-accent: #00d4aa;
193+
--oa-success: #34d399;
194+
--oa-error: #ff5f5f;
195+
/* ... */
196+
}
197+
```
198+
199+
## Testing
71200

72201
```bash
73-
# Run tests (when added)
202+
# Run all tests
74203
uv run pytest tests/ -v
75204

76-
# Check imports
77-
uv run python -c "from openadapt_viewer import generate_benchmark_html; print('OK')"
205+
# Run component tests
206+
uv run pytest tests/test_components/ -v
207+
208+
# Check imports work
209+
uv run python -c "from openadapt_viewer import screenshot_display, PageBuilder; print('OK')"
210+
```
211+
212+
## Integration Examples
213+
214+
### openadapt-ml (Training Dashboard)
215+
216+
```python
217+
from openadapt_viewer.builders import PageBuilder
218+
from openadapt_viewer.components import metrics_grid, screenshot_display
219+
220+
def generate_training_dashboard(state, config):
221+
builder = PageBuilder(title="Training", include_chartjs=True)
222+
builder.add_header(title="Training Dashboard", subtitle=f"Model: {state.model_name}")
223+
builder.add_section(metrics_grid([
224+
{"label": "Epoch", "value": state.epoch},
225+
{"label": "Loss", "value": f"{state.loss:.4f}"},
226+
]))
227+
return builder.render()
78228
```
229+
230+
### openadapt-evals (Benchmark Viewer)
231+
232+
```python
233+
from openadapt_viewer import generate_benchmark_html, BenchmarkRun
234+
235+
run = BenchmarkRun.from_directory("results/run_001/")
236+
generate_benchmark_html(run_data=run, output_path="benchmark.html")
237+
```
238+
239+
### openadapt-retrieval (Search Results)
240+
241+
```python
242+
from openadapt_viewer.builders import PageBuilder
243+
from openadapt_viewer.components import screenshot_display, selectable_list
244+
245+
def generate_retrieval_viewer(results):
246+
builder = PageBuilder(title="Search Results")
247+
for result in results:
248+
builder.add_section(f'''
249+
{screenshot_display(result.screenshot)}
250+
<div>Similarity: {result.score:.3f}</div>
251+
''')
252+
return builder.render()
253+
```
254+
255+
## Related Projects
256+
257+
- [openadapt-ml](../openadapt-ml/) - ML engine, uses this for dashboards
258+
- [openadapt-evals](../openadapt-evals/) - Benchmark evaluation, uses this for viewers
259+
- [openadapt-capture](../openadapt-capture/) - Recording capture
260+
- [openadapt-retrieval](../openadapt-retrieval/) - Demo retrieval

0 commit comments

Comments
 (0)