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.
git clone --depth 1 https://github.com/ginlix-ai/LangAlpha /tmp/interactive-dashboard && cp -r /tmp/interactive-dashboard/skills/interactive-dashboard ~/.claude/skills/interactive-dashboardSKILL.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) BashIntegrated 3-statement financial model: linked income statement, balance sheet, and cash flow
Create and manage scheduled and price-triggered automations.
Event tracker: earnings dates, economic releases, conferences, regulatory events
Investment deck QC: number consistency, data-narrative alignment, IB language, formatting audit
Financial model audit: structural checks, formula validation, integrity testing
Competitive landscape analysis: positioning, scorecards, moat assessment, market share trends
Comparable company analysis: operating metrics, valuation multiples, peer benchmarking
DCF valuation: free cash flow projections, WACC, terminal value, sensitivity analysis