← Back to Skills Marketplace
gangtao

Pulsebot App Builder

by Gang Tao · GitHub ↗ · v1.0.0 · MIT-0
cross-platform ✓ Security Clean
41
Downloads
0
Stars
0
Active Installs
1
Versions
Install in OpenClaw
/install pulsebot-app-builder
Description
Build real-time Timeplus data processing and analysis applications in Pulsebot. Creates pure frontend HTML/JavaScript apps that connect directly to Timeplus...
README (SKILL.md)

Pulsebot App Builder

Use this skill whenever the user asks to build a data processing application, pipeline visualizer, real-time dashboard, streaming analytics app, or any frontend tool that queries or visualizes data from Timeplus Proton.

Overview

You will produce a single self-contained HTML file that:

  1. Queries Timeplus Proton directly via @timeplus/proton-javascript-driver loaded from unpkg CDN
  2. Visualizes streaming data using @timeplus/vistral loaded from unpkg CDN
  3. Follows the Timeplus App Style Guide (dark theme, brand colors, clean layout)
  4. Requires no npm install, no build step — open the HTML file directly in a browser

Key architecture: Both @timeplus/proton-javascript-driver and @timeplus/vistral ship UMD builds. Import them via \x3Cscript src="https://unpkg.com/..."> tags. The Proton driver connects directly to the agent proxy running at localhost:8001.


Step-by-Step Workflow

Step 1 — Clarify Requirements

Before writing any code, confirm:

  • What stream(s) or table(s) to query (name, schema if known)
  • What kind of visualization: time series, bar/column, table, single value, or multi-panel
  • Whether the query is streaming (SELECT ... FROM stream_name) or historical (SELECT ... FROM table(stream_name))
  • Any filters, aggregations, or window functions needed

If the user doesn't know the schema, suggest running DESCRIBE stream_name or SHOW STREAMS first.


Step 2 — Use the HTML Template

All Timeplus apps follow this single-file HTML template. Read references/STYLE_GUIDE.md for style rules and references/VISTRAL_API.md for chart configuration options.

\x3C!DOCTYPE html>
\x3Chtml lang="en">
\x3Chead>
  \x3Cmeta charset="UTF-8" />
  \x3Cmeta name="viewport" content="width=device-width, initial-scale=1.0" />
  \x3Ctitle>My Timeplus App\x3C/title>

  \x3C!-- 1. React (required by Vistral) -->
  \x3Cscript src="https://unpkg.com/react@18/umd/react.production.min.js">\x3C/script>
  \x3Cscript src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js">\x3C/script>

  \x3C!-- 2. Vistral peer dependencies — load BEFORE vistral -->
  \x3Cscript src="https://unpkg.com/lodash@4/lodash.min.js">\x3C/script>
  \x3Cscript src="https://unpkg.com/[email protected]/dist/ramda.min.js">\x3C/script>
  \x3Cscript src="https://unpkg.com/@antv/g2@5/dist/g2.min.js">\x3C/script>
  \x3Cscript src="https://unpkg.com/@antv/s2@2/dist/s2.min.js">\x3C/script>

  \x3Cscript>
    // Prevent jsx-runtime from crashing on process.env access
    window.process = window.process || {
      env: { NODE_ENV: "production" }
    };

    // Some builds also expect global
    window.global = window.global || window;
  \x3C/script>

  \x3C!-- 3. Vistral UMD — exposes window.Vistral -->
  \x3Cscript src="https://unpkg.com/@timeplus/vistral/dist/index.umd.min.js">\x3C/script>

  \x3C!-- 4. Proton JavaScript Driver UMD — exposes window.ProtonDriver -->
  \x3Cscript src="https://unpkg.com/@timeplus/proton-javascript-driver/dist/index.umd.js">\x3C/script>

  \x3Cstyle>
    /* Timeplus design tokens */
    :root {
      --tp-bg-primary: #0f1117;
      --tp-bg-secondary: #1a1d27;
      --tp-bg-card: #1e2235;
      --tp-bg-hover: #252a3a;
      --tp-accent-primary: #7c6af7;
      --tp-accent-secondary: #4fc3f7;
      --tp-accent-success: #4caf82;
      --tp-accent-warning: #f7a84f;
      --tp-accent-danger: #f76f6f;
      --tp-text-primary: #e8eaf6;
      --tp-text-secondary: #9ea3b8;
      --tp-text-muted: #5c6380;
      --tp-border: #2e3450;
      --tp-font-mono: 'JetBrains Mono', 'Fira Code', monospace;
      --tp-font-sans: 'Inter', system-ui, sans-serif;
      --tp-radius-lg: 12px;
      --tp-shadow: 0 2px 12px rgba(0,0,0,0.4);
    }
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body { background: var(--tp-bg-primary); color: var(--tp-text-primary); font-family: var(--tp-font-sans); font-size: 14px; line-height: 1.5; }
    .tp-app { display: flex; flex-direction: column; height: 100vh; overflow: hidden; }
    .tp-header { display: flex; align-items: center; gap: 12px; padding: 0 24px; height: 56px; background: var(--tp-bg-secondary); border-bottom: 1px solid var(--tp-border); flex-shrink: 0; }
    .tp-header-title { font-size: 16px; font-weight: 600; }
    .tp-header-badge { font-size: 11px; padding: 2px 8px; background: var(--tp-accent-primary); color: white; border-radius: 99px; font-weight: 500; }
    .tp-status { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--tp-text-muted); margin-left: auto; }
    .tp-status-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--tp-text-muted); }
    .tp-status-dot.connected { background: var(--tp-accent-success); }
    .tp-status-dot.error { background: var(--tp-accent-danger); }
    .tp-main { flex: 1; overflow: auto; padding: 20px 24px; display: grid; gap: 16px; }
    .tp-card { background: var(--tp-bg-card); border: 1px solid var(--tp-border); border-radius: var(--tp-radius-lg); padding: 16px; box-shadow: var(--tp-shadow); }
    .tp-card-title { font-size: 13px; font-weight: 600; color: var(--tp-text-secondary); text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 12px; }
    .tp-error { background: rgba(247,111,111,0.1); border: 1px solid var(--tp-accent-danger); border-radius: 8px; padding: 12px 16px; color: var(--tp-accent-danger); font-size: 13px; display: none; }
    .chart-container { min-height: 300px; }
  \x3C/style>
\x3C/head>
\x3Cbody>
  \x3Cdiv class="tp-app">
    \x3Cheader class="tp-header">
      \x3Cspan class="tp-header-title">My Stream App\x3C/span>
      \x3Cspan class="tp-header-badge">LIVE\x3C/span>
      \x3Cspan class="tp-status">
        \x3Cspan class="tp-status-dot" id="status-dot">\x3C/span>
        \x3Cspan id="status-text">Connecting…\x3C/span>
      \x3C/span>
    \x3C/header>
    \x3Cmain class="tp-main">
      \x3Cdiv class="tp-error" id="error-panel">\x3C/div>
      \x3Cdiv class="tp-card">
        \x3Cdiv class="tp-card-title">Live Stream\x3C/div>
        \x3Cdiv class="chart-container" id="chart">\x3C/div>
      \x3C/div>
    \x3C/main>
  \x3C/div>

  \x3Cscript>
    // ---- Library globals ----
    // proton-javascript-driver UMD exposes window.ProtonDriver
    const { ProtonClient } = window.ProtonDriver;
    // vistral UMD exposes window.Vistral; React/ReactDOM are also on window
    const { Vistral, React, ReactDOM } = window;
    const { StreamChart } = Vistral;

    // ---- Configuration ----
    const SQL = 'SELECT * FROM my_stream';  // TODO: replace with your SQL

    // Connect to the Proton proxy running on the agent at localhost:8001
    const client = new ProtonClient({ host: 'localhost', port: 8001 });

    // ---- State ----
    let columns = [];
    let rows = [];
    let chartRoot = null;

    // ---- Helpers ----
    function setStatus(state, message) {
      document.getElementById('status-dot').className = 'tp-status-dot ' + (state || '');
      document.getElementById('status-text').textContent = message;
    }

    function showError(msg) {
      const panel = document.getElementById('error-panel');
      panel.style.display = msg ? 'block' : 'none';
      panel.textContent = msg || '';
      if (msg) setStatus('error', 'Error');
    }

    function inferColumns(row) {
      return Object.entries(row).map(([name, value]) => ({
        name,
        type: typeof value === 'number' ? 'float64'
            : String(value).match(/^\d{4}-\d{2}-\d{2}/) ? 'datetime64'
            : 'string',
      }));
    }

    // ---- Render chart ----
    function renderChart() {
      if (columns.length === 0) return;
      if (!chartRoot) chartRoot = ReactDOM.createRoot(document.getElementById('chart'));

      // TODO: adjust config for your data — see references/VISTRAL_API.md
      const config = {
        chartType: 'line',
        xAxis: columns[0].name,
        yAxis: columns[1]?.name ?? columns[0].name,
        temporal: { mode: 'axis', field: columns[0].name, range: 60 },
      };

      chartRoot.render(
        React.createElement(StreamChart, {
          config,
          data: { columns, data: rows, isStreaming: true },
          theme: 'dark',
        })
      );
    }

    // ---- Main query loop ----
    async function runQuery() {
      try {
        setStatus('', 'Connecting…');
        const { rows: stream, abort } = await client.query(SQL);
        setStatus('connected', 'Connected');

        // Expose abort so it can be called externally if needed
        window.stopQuery = abort;

        for await (const row of stream) {
          if (columns.length === 0) columns = inferColumns(row);
          rows = [...rows.slice(-999), row];  // keep last 1000 rows
          renderChart();
        }

        setStatus('', 'Stream ended');
      } catch (err) {
        showError(err.message);
      }
    }

    runQuery();
  \x3C/script>
\x3C/body>
\x3C/html>

Step 3 — Proton Connection

Read references/PROTON_DRIVER.md for the full driver API.

The driver connects directly to the Proton proxy at localhost:8001. UMD global: window.ProtonDriver.

const { ProtonClient } = window.ProtonDriver;

const client = new ProtonClient({ host: 'localhost', port: 8001 });

const { rows, abort } = await client.query('SELECT * FROM my_stream');

for await (const row of rows) {
  // row is a plain JavaScript object: { col1: val1, col2: val2, ... }
  console.log(row);
}

// To cancel a running streaming query:
abort();

Step 4 — Visualization with Vistral

Read references/VISTRAL_API.md for the complete component reference.

Vistral UMD global: window.Vistral. Key components: StreamChart, SingleValueChart, DataTable.

All charts are rendered via React (React.createElement). Always pass theme="dark".

Quick chart config reference:

Chart type chartType Best for temporal mode
Time series 'line' or 'area' Scrolling metrics over time 'axis'
Horizontal bars 'bar' Ranked categories, latest snapshot 'frame'
Vertical bars 'column' Categorical comparison 'frame'
KPI tile 'singleValue' One big number 'frame'
Live table 'table' Raw rows / mutable state 'key'
Geo points 'geo' Location data 'key'

Step 5 — Apply Timeplus Style Guide

Read references/STYLE_GUIDE.md for full rules. Core rules always applied:

  • Dark background #0f1117 for page, #1e2235 for cards
  • Purple #7c6af7 for primary accents
  • Inter font for UI, monospace for data values
  • Cards with border-radius: 12px and border #2e3450
  • Header 56px with LIVE badge and connection status indicator

Step 6 — Deliver the App

  1. Write the complete single-file HTML, filling in the correct SQL and chart config
  2. use workspace_create_app to create the app
  3. Present the file — it opens directly in a browser with no server or build step needed

For PulseBot deployment:

workspace_create_app(
  session_id=session_id,
  task_name="My Stream App",
  html=open("my-app.html").read()
)

SQL Patterns

Load the timeplus-sql-guide skill for all SQL questions:

  • Streaming vs historical queries
  • Window functions (tumble, hop, session)
  • Aggregations, joins, filters
  • Schema introspection (DESCRIBE, SHOW STREAMS)

Checklist Before Delivering

  • Single HTML file — no separate JS/CSS files, no package.json, no build step
  • Script load order: React → ReactDOM → lodash → ramda → @antv/g2 → @antv/s2 → vistral → proton-javascript-driver
  • window.ProtonDriver destructured for ProtonClient
  • window.Vistral destructured for StreamChart (and other components as needed)
  • ProtonClient configured with { host: 'localhost', port: 8001 }
  • SQL uses correct Proton streaming syntax
  • columns inferred from first received row using inferColumns()
  • Chart config.temporal uses correct mode for the query type
  • Status indicator updates on connect, stream end, and error
  • abort() stored on window.stopQuery or similar
  • CSS uses Timeplus design tokens (CSS variables) from the style template
Usage Guidance
Install only if you intend to build Timeplus Proton/Pulsebot browser apps. Review generated HTML before opening it, especially the SQL query, localhost:8001 access, and third-party unpkg script tags; use trusted or pinned/local dependencies if your Proton data is sensitive.
Capability Assessment
Purpose & Capability
The skill consistently describes creating single-file HTML/JavaScript Timeplus Proton dashboards and visualizations using Vistral and the Proton JavaScript driver.
Instruction Scope
The activation wording includes broad dashboard and data-processing phrases, but the artifacts repeatedly anchor the workflow to Timeplus Proton/Pulsebot and require clarifying streams, SQL, and chart needs before coding.
Install Mechanism
The package contains markdown instructions and references only; no executable installer, background worker, package script, or automatic local mutation was found.
Credentials
Generated apps are expected to load JavaScript from unpkg and connect to localhost:8001, which is proportionate to the single-file Timeplus app purpose but should be understood by users.
Persistence & Privilege
No persistence, privilege escalation, credential harvesting, deletion, or background execution is instructed; the generated app exposes an abort handle for stopping a stream.
How to Use
  1. Make sure OpenClaw is installed (local or Docker)
  2. Run the install command in chat: /install pulsebot-app-builder
  3. After installation, invoke the skill by name or use /pulsebot-app-builder
  4. Provide required inputs per the skill's parameter spec and get structured output
Version History
v1.0.0
pulsebot-app-builder 1.0.0 — initial release - Build Timeplus data processing apps via Pulsebot, outputting a single self-contained HTML/JavaScript file. - No npm install or bundler needed; all dependencies loaded as UMD modules via CDN. - Connects directly to Timeplus Proton using @timeplus/proton-javascript-driver. - Visualizes live and historical streaming data with @timeplus/vistral, following the official Timeplus UI style guide. - Designed for flexible frontend app creation: dashboards, visualizers, and streaming analytics.
Metadata
Slug pulsebot-app-builder
Version 1.0.0
License MIT-0
All-time Installs 0
Active Installs 0
Total Versions 1
Frequently Asked Questions

What is Pulsebot App Builder?

Build real-time Timeplus data processing and analysis applications in Pulsebot. Creates pure frontend HTML/JavaScript apps that connect directly to Timeplus... It is an AI Agent Skill for Claude Code / OpenClaw, with 41 downloads so far.

How do I install Pulsebot App Builder?

Run "/install pulsebot-app-builder" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.

Is Pulsebot App Builder free?

Yes, Pulsebot App Builder is completely free, licensed under MIT-0. You can download, install and use it at no cost.

Which platforms does Pulsebot App Builder support?

Pulsebot App Builder is cross-platform and runs anywhere OpenClaw / Claude Code is available (cross-platform).

Who created Pulsebot App Builder?

It is built and maintained by Gang Tao (@gangtao); the current version is v1.0.0.

💬 Comments