Skip to main content
ClaudeWave
Skill1.6k estrellas del repoactualizado yesterday

interactive-dashboard

The interactive-dashboard skill builds real-time web dashboards, trackers, and visualizations served through a preview URL, enabling users to interact with dynamic financial data through filtering, drill-downs, and live updates. Use this skill when users request dashboards, portfolio monitors, sector heatmaps, or interactive web apps rather than static charts or reports.

Instalar en Claude Code
Copiar
git clone --depth 1 https://github.com/ginlix-ai/LangAlpha /tmp/interactive-dashboard && cp -r /tmp/interactive-dashboard/skills/interactive-dashboard ~/.claude/skills/interactive-dashboard
Después abre una sesión nueva de Claude Code; el skill carga automáticamente.

SKILL.md

# Interactive Dashboard

Build interactive web dashboards inside the sandbox and expose them to the user via `GetPreviewUrl`. Use this skill for any request involving dashboards, trackers, monitors, live visualizations, or interactive web apps.

## When to Use

Use this skill for a **live, served web app** — one that needs a running server, not a single file:

- User asks for a **dashboard**, **tracker**, or **monitor** that **refreshes live data** (polling, auto-update)
- The app needs **server-side logic** — filtering/screening over a large dataset, on-demand fetches, computed endpoints
- **Multi-page / routed** apps, or anything that needs React-level component interactivity
- The dataset is **too large to embed** in a single HTML file
- User explicitly says "preview", "web view", "web app", or wants it running at a URL

**Do NOT use if:**
- User wants a **self-contained HTML report** — even an *interactive* one (sortable tables, tabs, hover/zoom charts) over a **data snapshot**. That's `.agents/skills/html-report/SKILL.md`: one file in `results/`, keepable, printable, PDF-exportable, share-linkable. Interactivity by itself does **not** require a dashboard.
- User wants a **static chart image** → matplotlib/plotly `savefig`.
- User wants an **in-chat figure** → `inline-widget` (`ShowWidget`).

### Dashboard vs. HTML Report

Both can be interactive, so the divide is **live served app vs. self-contained snapshot file**, not static vs. interactive:

| | interactive-dashboard (this skill) | html-report |
|---|---|---|
| Delivery | A **running server**, exposed via `GetPreviewUrl` | One **`.html` file** in `results/` |
| Data | **Live / refreshing**, fetched from a backend; large datasets OK | A **snapshot** embedded in the file |
| Interactivity | Full app — routing, server-side filtering, live updates | Client-side over the snapshot — sort, filter, tabs, chart hover/zoom |
| Keep / print / share | A URL, live only while the workspace runs | Downloadable, PDF-exportable, share-linkable as one artifact |
| Pick when | Data must be live, or compute/scale needs a server | The answer is a deliverable the user keeps |

## Architecture

Choose the tier based on complexity:

| Tier | When | Stack | Serve command |
|------|------|-------|---------------|
| **Simple** | Snapshot-at-load data, few charts, no backend logic (still served via preview URL) | Self-contained HTML + CDN libs | `python -m http.server 8050 --bind 0.0.0.0` |
| **FastAPI + HTML** | Live data refresh, server-side logic, no React needed | FastAPI serves `static/` + `fetch()` polling | `bash start.sh` |
| **Complex** | Filtering, routing, component interactivity, multi-page | FastAPI backend + Vite/React frontend | `bash start.sh` |

**Decision rule:** Start with Simple. Escalate to FastAPI + HTML when user needs live data refresh or server-side logic. Escalate to Complex only when user needs React-level component interactivity, client-side routing, or a multi-page SPA.

**Port convention:** Use port **8050** (default). Range 8050-8059 for dashboards.

### CSP / Iframe Safety

The preview iframe enforces Content Security Policy (CSP). Certain patterns are **silently blocked** — no error banner, just dead UI elements. Always use the safe alternatives:

| Blocked pattern | Safe alternative |
|-----------------|-----------------|
| `<button onclick="fn()">` | `el.addEventListener('click', fn)` |
| `<div onmouseover="fn()">` | `el.addEventListener('mouseover', fn)` |
| Any `on*="..."` HTML attribute | `el.addEventListener(event, fn)` |
| `innerHTML` with `onclick` | `document.createElement()` + `addEventListener` |
| `eval("code")` | Direct function calls |
| `new Function("code")` | Named function declarations |
| `setTimeout("code string", ms)` | `setTimeout(fn, ms)` (function reference) |
| `<a href="javascript:...">` | `<a href="#" data-action="...">` + `addEventListener` |

**Quick self-check** — run before serving to catch violations:

```python
import subprocess
result = subprocess.run(
    ["grep", "-rnE", r'on(click|input|change|focus|blur|submit|load|error|mouse|key)\s*=',
     "work/dashboard/"],
    capture_output=True, text=True
)
if result.stdout.strip():
    raise RuntimeError(f"CSP-unsafe inline handlers found:\n{result.stdout}")
```

**Template literal hygiene** — when building HTML strings in JS template literals, CSS semicolons inside `${}` expressions cause silent parse failures:

```javascript
// BAD — semicolon inside ${} terminates the expression early
const el = `<div style="color:${positive ? 'green' : 'red'; font-weight:600}">`;

// GOOD — close the expression first, then continue the attribute string
const el = `<div style="color:${positive ? 'green' : 'red'};font-weight:600">`;
```

Rule: **never put a CSS semicolon inside `${}`** — always close `}` before the semicolon.

### How Preview Serving Works

`GetPreviewUrl` is a **platform-level tool** available only to the main agent runtime. It is NOT a Python function — do not `import` it or call it from `execute_code`. The agent invokes it as a tool call.

When you call `GetPreviewUrl(port, command, title)`:

1. The **command is persisted to the database** automatically
2. The platform starts the command in a dedicated sandbox session for that port
3. It polls until the port is listening, then generates a signed URL
4. If the port is **already reachable**, the command start is skipped entirely

**Sub-agent fallback:** Sub-agents cannot call `GetPreviewUrl`. Instead, build the dashboard files, start the server for verification, then return the serve details so the orchestrating agent can call `GetPreviewUrl`.

**All tiers** — use the Bash tool with `run_in_background=true` to start the server:

```bash
# Simple tier — Bash tool with run_in_background=true
cd work/<task> && python -m http.server 8050 --bind 0.0.0.0

# Docker tiers — Bash tool with run_in_background=true
cd work/<task> && bash start.sh
```

Then verify it's up in a separate (foreground) Bash