timeplus-app-builder

v1.0.0

Build real-time Timeplus data processing and analysis applications. Creates pure frontend HTML/JavaScript apps that connect directly to Timeplus Proton via @...

0· 273·0 current·0 all-time
byGang Tao@gangtao
Security Scan
VirusTotalVirusTotal
Benign
View report →
OpenClawOpenClaw
Benign
high confidence
Purpose & Capability
Name/description (build Timeplus frontend apps) match the instructions: generate a single self-contained HTML that loads @timeplus/proton-javascript-driver and @timeplus/vistral from CDN and connects to Proton via localhost:8001. No unrelated binaries, env vars, or other services are requested.
Instruction Scope
SKILL.md stays on-topic: it instructs how to build the HTML, how to load UMD libs, and how to query Proton via the driver. It explicitly targets localhost:8001 (the agent proxy) — which is consistent with a frontend that talks to a local proxy. The instructions do not request reading local files, environment variables, or sending collected data to unexpected external endpoints in the skill text.
Install Mechanism
No install spec (instruction-only) — low disk footprint. The generated HTML relies on loading multiple third-party UMD packages at runtime from unpkg.com. This is expected for a no-build single-file app but means the rendered app will execute remote CDN code in the user's browser when opened.
Credentials
The skill requests no environment variables or credentials. Its only external interaction is to the Proton proxy on localhost:8001, which is appropriate for the stated goal of querying Timeplus Proton.
Persistence & Privilege
The skill is instruction-only, not always-on, does not request persistent system changes, and does not modify other skills or global agent settings. Autonomous invocation being allowed is the platform default and not a concern by itself.
Assessment
This skill appears internally consistent for building single-file Timeplus frontends. Before using/installing: (1) be aware the produced HTML will load third-party scripts from unpkg.com at runtime — those remote scripts run in your browser and should be trusted (verify package names/versions). (2) The app connects to a Proton proxy at localhost:8001 — ensure that host/port in your environment is the intended Timeplus agent proxy and does not expose sensitive data. (3) No credentials are requested by the skill, but if your Proton deployment requires authentication you should avoid embedding secrets in the HTML; instead use a secure proxy or server-side component.

Like a lobster shell, security has layers — review code before you run it.

latestvk97es7aqa4fgpcstzpsqwc7ad1829n0y
273downloads
0stars
1versions
Updated 1mo ago
v1.0.0
MIT-0

Timeplus 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 <script 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.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>My Timeplus App</title>

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

  <!-- 2. Vistral peer dependencies — load BEFORE vistral -->
  <script src="https://unpkg.com/lodash@4/lodash.min.js"></script>
  <script src="https://unpkg.com/ramda@0.29/dist/ramda.min.js"></script>
  <script src="https://unpkg.com/@antv/g2@5/dist/g2.min.js"></script>
  <script src="https://unpkg.com/@antv/s2@2/dist/s2.min.js"></script>

  <script>
    // 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;
  </script>

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

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

  <style>
    /* 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; }
  </style>
</head>
<body>
  <div class="tp-app">
    <header class="tp-header">
      <span class="tp-header-title">My Stream App</span>
      <span class="tp-header-badge">LIVE</span>
      <span class="tp-status">
        <span class="tp-status-dot" id="status-dot"></span>
        <span id="status-text">Connecting…</span>
      </span>
    </header>
    <main class="tp-main">
      <div class="tp-error" id="error-panel"></div>
      <div class="tp-card">
        <div class="tp-card-title">Live Stream</div>
        <div class="chart-container" id="chart"></div>
      </div>
    </main>
  </div>

  <script>
    // ---- 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();
  </script>
</body>
</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 typechartTypeBest fortemporal 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

Comments

Loading comments...