npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

streamboard

v0.0.7

Published

A CLI that gives AI agents a live browser canvas.

Readme

Streamboard

A CLI that gives AI agents a live browser canvas. Push data, UI appears. Beautiful, animated, real-time.

Install / Run

npx --yes streamboard serve

The published package vendors Bun as a dependency and ships a prebuilt browser UI, so npx --yes streamboard serve does not run a second hidden npm install for the frontend.

If you use Streamboard often, install it globally for faster startup on subsequent runs:

npm install -g streamboard
streamboard serve

Environment variables

| Variable | Effect | |----------|--------| | STREAMBOARD_STATIC=1 | Serve the prebuilt web/dist UI (single port) even when a dev checkout has web/src — useful to preview a production build locally. Requires web/dist to exist. | | STREAMBOARD_FORCE_VITE=1 | Force the Vite dev server + separate WebSocket port, even if web/dist is present. |

npm publish runs prepack, which builds web/ with node scripts/prepack-web.mjs (Bun if it is on PATH, otherwise npm).

Quick Start

# Start the canvas server (opens browser automatically)
npx --yes streamboard serve

# In another terminal, push components:
npx --yes streamboard push header '{"component":"header","title":"My Report","subtitle":"Generated by AI"}'

For live multi-slot dashboards, stream JSONL so the canvas updates as each message is generated:

npx --yes streamboard stream --stdin <<'EOF'
{"type":"push","id":"header","data":{"component":"header","title":"Live report","subtitle":"Streaming from stdin"}}
{"type":"push","id":"notes","data":{"component":"markdown","content":"## Status\n\nFirst chunk arrived."}}
{"type":"notify","text":"Canvas ready","variant":"success"}
EOF

Commands

serve

Starts the WebSocket server and browser UI. In a published install this serves the prebuilt static app on one port; in a local repo checkout with web/src, it uses Vite for hot reload. Then opens your browser unless --no-open is set.

Other commands auto-detect the running instance from per-project runtime state stored in your user temp/cache area, so normal usage does not create a repo-local .canvas.pid.

npx --yes streamboard serve              # start on default port 3001
npx --yes streamboard serve --port 4000  # custom port
npx --yes streamboard serve --no-open    # don't open browser

push <id> <json>

Push a component to the canvas. Each slot has a unique ID — pushing to the same ID replaces it.

npx --yes streamboard push myslot '{"component":"header","title":"Hello"}'
npx --yes streamboard push notes --md "# Markdown content here"
npx --yes streamboard push data --file ./results.json
echo '{"component":"table",...}' | npx --yes streamboard push tbl --stdin

| Flag | Description | |-----------|-------------------------------------| | --md | Wrap text as a markdown component | | --file | Read JSON from a file | | --stdin | Read JSON from stdin | | --port | Server port (default: 3001) |

stream

Send multiple canvas messages over one persistent WebSocket connection.

  • --stdin is the live path: send JSONL with one message object per line and Streamboard forwards each line as it arrives.
  • --file reads a prepared JSON array or JSONL file.
  • --messages accepts a short inline JSON array.
  • --validate parses and validates messages without sending them.
  • --delay adds a delay in milliseconds between messages.
# Progressive JSONL from stdin
npx --yes streamboard stream --delay 300 --stdin <<'EOF'
{"type":"push","id":"header","data":{"component":"header","title":"Agent Run"}}
{"type":"push","id":"notes","data":{"component":"markdown","content":"Streaming live updates."}}
EOF

# Prepared batch payload from a file
npx --yes streamboard stream --file ./dashboard.json

# Validate without sending
npx --yes streamboard stream --validate --stdin <<'EOF'
{"type":"notify","text":"Check me","variant":"success"}
EOF

clear [id]

Remove slots from the canvas.

npx --yes streamboard clear header    # remove one slot
npx --yes streamboard clear --all     # clear everything

Components

Every push payload needs a "component" field to tell the canvas what to render.

header

{
  "component": "header",
  "title": "Quarterly Report",
  "subtitle": "Q1 2026 — Auto-generated",
  "badges": [
    { "label": "Live", "variant": "default" },
    { "label": "Draft", "variant": "secondary" }
  ]
}

kpi-cards

{
  "component": "kpi-cards",
  "cards": [
    { "label": "Revenue", "value": "$1.2M", "trend": "↑ 12%", "status": "green" },
    { "label": "Latency", "value": "142ms", "trend": "↓ 3%", "status": "yellow" },
    { "label": "Errors", "value": "0.02%", "status": "green" }
  ]
}

Cards animate in with a staggered cascade. status shows a colored dot (green/yellow/red). trend is colored based on direction (↑/+ = green, ↓/- = red).

chart

Supports line, bar, area, and donut variants.

{
  "component": "chart",
  "variant": "area",
  "title": "Revenue Over Time",
  "xKey": "month",
  "series": [
    { "key": "revenue", "label": "Revenue", "color": "#3b82f6" },
    { "key": "profit", "label": "Profit", "color": "#22c55e" }
  ],
  "data": [
    { "month": "Jan", "revenue": 400, "profit": 240 },
    { "month": "Feb", "revenue": 300, "profit": 139 },
    { "month": "Mar", "revenue": 500, "profit": 380 }
  ]
}

table

{
  "component": "table",
  "title": "Service Status",
  "columns": [
    { "key": "service", "label": "Service" },
    { "key": "status", "label": "Status", "variant": "badge" },
    { "key": "latency", "label": "Latency", "align": "right" }
  ],
  "rows": [
    { "service": "API Gateway", "status": "healthy", "latency": "12ms" },
    { "service": "Database", "status": "active", "latency": "3ms" }
  ]
}

Columns with "variant": "badge" render values as colored badges. Status-like values (healthy, active, error, failed) get automatic coloring.

markdown

{
  "component": "markdown",
  "content": "# Hello World\n\nThis supports **GFM** including:\n- Lists\n- `code`\n- Tables\n- Blockquotes"
}

Or use the --md shorthand:

npx --yes streamboard push notes --md "# Analysis Complete\n\nFound **3 issues**."

progress

{
  "component": "progress",
  "title": "Pipeline",
  "steps": [
    { "label": "Fetching data", "status": "done" },
    { "label": "Running analysis", "status": "active" },
    { "label": "Generating report", "status": "pending" }
  ]
}

Steps show as a vertical list with animated checkmarks, a spinning indicator for active, and muted dots for pending.

code

{
  "component": "code",
  "title": "Query",
  "language": "sql",
  "code": "SELECT users.name, COUNT(orders.id)\nFROM users\nJOIN orders ON orders.user_id = users.id\nGROUP BY users.name\nORDER BY COUNT(orders.id) DESC;",
  "highlights": [
    { "lines": [3, 4], "label": "Join clause", "color": "#3b82f6" }
  ]
}

Highlighted lines get a colored left border and floating label.

Layout Control

Push a special _layout slot to control the grid:

# Two-column layout
npx --yes streamboard push _layout '{"component":"_layout","columns":2}'

# Control slot order
npx --yes streamboard push _layout '{"component":"_layout","columns":2,"order":["header","kpis","chart1","table1"]}'

Architecture

Published: npx --yes streamboard serve   Prebuilt UI + WebSocket on one port
───────────────────────────────────────  ─────────────────────────────────────
  → Bun.serve (static + /ws + /state)    Browser uses same-origin ws://…/ws

Local dev: bun cli.ts serve (repo)       Vite (:5173) + WebSocket (separate port)
───────────────────────────────────────  ─────────────────────────────────────
  → spawns Vite with VITE_WS_PORT        React connects to ws://localhost:<ws>/ws

npx --yes streamboard stream --stdin     On each JSONL message:
  → keeps one WS connection open      → updates state Map incrementally
  → parses one JSON object per line   → React re-renders as messages arrive

npx --yes streamboard push <id> <json>  On WS "push" message:
  → connects to WS                  → updates state Map
  → sends message                   → React re-renders with animation
  → disconnects

Runtime discovery                     Per-project temp/cache record
  → written on serve start           → lets later CLI calls auto-find the port
  → cleaned up on stop               → avoids repo-local runtime files

npx --yes streamboard clear       On WS "clear" message:
  → sends clear message             → removes slot(s) with exit animation

The browser auto-reconnects if the server restarts — no data loss on reconnect since the server re-sends full state.

Development

# Install dependencies
bun install
cd web && bun install

# Start everything
bun cli.ts serve

# Build the web frontend
cd web && bun run build

Tech Stack

  • CLI/Server: Bun, citty, @clack/prompts
  • Frontend: React 19, Vite, Tailwind v4, shadcn/ui, Framer Motion, Recharts
  • Protocol: WebSocket (JSON messages)