# Dashboard Preview System

## Overview

The Infinite Agents dashboard now features a **hybrid preview system** combining static screenshots with live iframe previews for the best balance of performance and interactivity.

## Features

### 📸 Static Screenshot Thumbnails
- **200px preview** in every demo card
- **Zero performance overhead** (standard image loading)
- **Instant visual feedback** - no waiting
- **Fallback placeholder** if screenshot is missing

### 👁️ Live Iframe Preview on Hover
- **Hover for 800ms** to trigger live preview modal
- **Full-sized interactive demo** in modal (90vw × 80vh)
- **Only one iframe at a time** - efficient memory usage
- **Close with**: Escape key, backdrop click, or close button

## Quick Start

### 1. Install Dependencies
```bash
npm install
npx playwright install chromium
```

### 2. Start Development Server
```bash
npm run server
# or
python3 -m http.server 8889
```

### 3. Generate Screenshots
```bash
# All demos (~5-8 minutes for 107 demos)
npm run screenshots

# Or by category
npm run screenshots:threejs
npm run screenshots:sdg
npm run screenshots:ui
```

### 4. View Dashboard
Open http://localhost:8889/ in your browser.

## How It Works

```
┌─────────────────────────────────────────┐
│  User hovers over demo card (800ms)     │
└───────────────┬─────────────────────────┘
                │
                ▼
┌─────────────────────────────────────────┐
│  Modal appears with loading spinner     │
└───────────────┬─────────────────────────┘
                │
                ▼
┌─────────────────────────────────────────┐
│  Iframe loads demo (single instance)    │
└───────────────┬─────────────────────────┘
                │
                ▼
┌─────────────────────────────────────────┐
│  User can interact with live demo       │
└───────────────┬─────────────────────────┘
                │
                ▼
┌─────────────────────────────────────────┐
│  Close modal → iframe unloaded          │
└─────────────────────────────────────────┘
```

## Screenshot Generation

### Directory Structure
```
infinite-agents/
├── screenshots/                    # Auto-generated
│   ├── threejs_viz_threejs_viz_1.html.png
│   ├── sdg_viz_sdg_viz_1.html.png
│   └── ...
├── generate_screenshots.js         # Generator script
├── package.json                    # NPM scripts
└── index.html                      # Dashboard
```

### Filename Convention
Screenshots are named by replacing `/` with `_`:
- `threejs_viz/threejs_viz_1.html` → `threejs_viz_threejs_viz_1.html.png`
- `src/ui_hybrid_5.html` → `src_ui_hybrid_5.html.png`
- `mapbox_test/mapbox_globe_2/index.html` → `mapbox_test_mapbox_globe_2_index.html.png`

### Customizing Delays

Different demo types need different rendering times:

```javascript
// In generate_screenshots.js
const DEMO_CATEGORIES = {
    threejs: { delay: 3000 },   // WebGL needs time
    mapbox: { delay: 3000 },    // Tile loading
    sdg: { delay: 2000 },       // D3 force simulation
    d3: { delay: 1500 },        // SVG rendering
    uiSingle: { delay: 800 },   // Static/simple
};
```

## NPM Scripts

```bash
# Dashboard
npm run dashboard           # Regenerate index.html
npm run server             # Start HTTP server

# Screenshots
npm run screenshots        # All demos
npm run screenshots:threejs    # Three.js only
npm run screenshots:sdg        # SDG network only
npm run screenshots:d3         # D3 viz only
npm run screenshots:mapbox     # Mapbox globes only
npm run screenshots:devtools   # DevTools only
npm run screenshots:ui         # UI components only
```

## Performance Comparison

### Before (No Previews)
- Initial load: **~100KB**
- Memory: **~50MB**
- First paint: **<100ms**

### After (Hybrid System)
- Initial load: **~2-3MB** (includes all screenshots)
- Memory: **~80MB** (base) + **40MB** per active iframe
- First paint: **~200ms**
- Screenshot cache: Cached after first load
- Iframe: Only 1 active at a time, unloaded on close

### With 107 Demos
- **15-20MB** total screenshots (compressed PNG)
- **Zero impact** when browsing (screenshots cached)
- **Minimal impact** when hovering (single iframe)

## Workflow Integration

### After Generating New Demos
```bash
# 1. Generate demos with infinite loop
/project:infinite-web specs/threejs_visualization_progressive.md threejs_viz 5

# 2. Update dashboard data
python3 generate_index.py

# 3. Generate screenshots for new demos
npm run screenshots:threejs

# 4. Refresh browser
```

### Automated Script
```bash
#!/bin/bash
# update_all.sh

echo "📊 Updating dashboard..."
python3 generate_index.py

echo "📸 Generating screenshots..."
npm run screenshots

echo "✅ Complete! Refresh browser to see updates."
```

## Troubleshooting

### Screenshots Not Showing
**Problem:** Cards show 📸 placeholder icon
**Solution:**
```bash
# Check if screenshots directory exists
ls -la screenshots/

# Regenerate screenshots
npm run screenshots
```

### Server Not Running Error
**Problem:** `Server is not running on http://localhost:8889`
**Solution:**
```bash
# Start server in separate terminal
python3 -m http.server 8889
```

### Playwright Not Installed
**Problem:** `Error: Browser not found`
**Solution:**
```bash
npx playwright install chromium
```

### Modal Not Opening
**Problem:** Hover preview doesn't appear
**Solution:**
- Check browser console for errors
- Ensure you hover for 800ms (intentional delay)
- Try clicking card to open full demo

### Screenshots Look Wrong
**Problem:** Screenshots don't match current demo
**Solution:**
```bash
# Regenerate specific screenshot
node generate_screenshots.js --single=threejs_viz/threejs_viz_1.html

# Or regenerate all
npm run screenshots
```

## Advanced Usage

### Single Screenshot
```bash
node generate_screenshots.js --single=path/to/demo.html
```

### Custom Port
```bash
node generate_screenshots.js --port=3000
```

### Category Filter
```bash
node generate_screenshots.js --category=threejs
```

## Technical Details

### Card HTML Structure
```html
<div class="demo-card" data-path="threejs_viz/threejs_viz_1.html">
    <div class="demo-screenshot">
        <img src="screenshots/threejs_viz_threejs_viz_1.html.png"
             onerror="this.style.display='none'">
        <div class="demo-screenshot-placeholder">📸</div>
        <div class="demo-screenshot-overlay">
            <span>👁️ Hover to preview</span>
        </div>
    </div>
    <!-- ... rest of card -->
</div>
```

### Modal System
```javascript
// Single reusable modal
const previewModal = document.querySelector('.preview-modal');
const previewIframe = document.querySelector('.preview-iframe');

// Hover handler (800ms delay)
card.addEventListener('mouseenter', () => {
    hoverTimeout = setTimeout(() => {
        showPreview(path, title);
    }, 800);
});

// Unload iframe on close
function hidePreview() {
    previewModal.classList.remove('visible');
    setTimeout(() => {
        previewIframe.src = ''; // Free memory
    }, 300);
}
```

### Screenshot Capture
```javascript
// Playwright headless browser
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();

// Set viewport
await page.setViewportSize({ width: 1920, height: 1080 });

// Navigate and wait for render
await page.goto(url, { waitUntil: 'networkidle' });
await page.waitForTimeout(demo.delay);

// Capture viewport (not full page)
await page.screenshot({ path: screenshotPath, fullPage: false });
```

## Browser Compatibility

- **Chrome/Edge:** ✅ Full support
- **Firefox:** ✅ Full support
- **Safari:** ✅ Full support (backdrop-filter may vary)

## Future Improvements

- [ ] **WebP format** - 40% smaller file size
- [ ] **Lazy image loading** - Only load screenshots in viewport
- [ ] **Video previews** - For animated demos
- [ ] **Screenshot diff** - Only regenerate changed demos
- [ ] **Thumbnail optimization** - Lower resolution for cards
- [ ] **Progressive enhancement** - Work without screenshots

## Credits

Built for the **Infinite Agents** project using:
- Playwright for screenshot capture
- Vanilla JavaScript for modal system
- CSS Grid for responsive layout

---

**Documentation:** See [DASHBOARD.md](DASHBOARD.md) for complete guide
**Project:** [README.md](README.md)
