Skip to content

pondpilot/pondpilot-widget

Repository files navigation

PondPilot Widget

npm version npm downloads CDN hits License: MIT

Transform static SQL code blocks into interactive snippets powered by DuckDB WASM.

🎮 Try it live - Interactive examples and demos

Features

  • 🦆 DuckDB in the Browser - Full SQL analytics engine running locally
  • Zero Backend - Everything runs client-side in WebAssembly
  • 🎨 Syntax Highlighting - Beautiful SQL code formatting with cursor preservation
  • Instant Results - Execute queries with one click or Ctrl/Cmd+Enter
  • 🔧 Easy Integration - Works with any static site or documentation
  • 🧩 Configurable - Tweak selectors, auto-init, editable mode, and UI affordances
  • 🗂️ Init Queries - Install DuckDB extensions or run setup SQL once per page
  • ♻️ Reset Queries - Optionally run cleanup SQL when the editor resets
  • 🎨 Custom Themes - Extend light/dark defaults or register fully custom palettes
  • Accessible - Full ARIA support and keyboard navigation
  • 🎯 Lightweight - Only ~22KB minified, loads DuckDB on-demand
  • 🌙 Dark Mode - Automatic theme detection or manual control
  • 🔒 Secure - No data leaves the browser, CSP-compatible
  • 📁 Relative Paths - Automatic resolution of relative parquet file paths

Installation

CDN (Recommended)

The easiest way to get started is via CDN:

<!-- Latest version -->
<script src="https://unpkg.com/pondpilot-widget"></script>

<!-- Specific version (recommended for production) -->
<script src="https://unpkg.com/[email protected]"></script>

<!-- Alternative CDN -->
<script src="https://cdn.jsdelivr.net/npm/pondpilot-widget"></script>

Package Manager

For projects using a bundler:

NPM

npm install pondpilot-widget

Yarn

yarn add pondpilot-widget

Quick Start

1. Add the Script Tag

<script src="https://unpkg.com/pondpilot-widget"></script>

2. Mark Your SQL Code Blocks

<pre class="pondpilot-snippet">
SELECT
  'Hello World' as greeting,
  42 as answer,
  CURRENT_DATE as today;
</pre>

3. That's It!

The widget automatically transforms your static code blocks into interactive SQL editors.

Alternative: Using a Bundler

If you're using a bundler like Webpack, Vite, or Parcel:

import "pondpilot-widget";

Usage Examples

Basic Configuration

// Configure before initialization
window.PondPilot.config.theme = 'dark';
window.PondPilot.config.showPoweredBy = false;

// Or initialize with options
new PondPilot.Widget(element, {
  theme: 'dark',
  editable: false,
  showPoweredBy: false
});

Framework Integration

React

import { useEffect, useRef } from "react";
import "pondpilot-widget";

function SQLEditor({ sql, ...options }) {
  const ref = useRef(null);

  useEffect(() => {
    if (ref.current) {
      const widget = new window.PondPilot.Widget(ref.current, options);
      return () => widget.destroy();
    }
  }, []);

  return (
    <pre ref={ref} className="pondpilot-snippet">
      {sql}
    </pre>
  );
}

Advanced Configuration

The runtime exposes a global API with ergonomic helpers:

const { config, init, create, destroy, registerTheme, getConfig } = window.PondPilot;

// Merge default configuration
config({
  selector: "pre.sql-demo",
  baseUrl: "https://cdn.example.com/datasets",
  autoInit: false,
  initQueries: ["INSTALL httpfs", "LOAD httpfs"],
  resetQueries: ["DROP TABLE IF EXISTS scratch_table;"],
});

// Register a custom theme and apply it via data-theme or options.theme
registerTheme("sunset", {
  extends: "light",
  config: {
    bgColor: "#ffedd5",
    textColor: "#7c2d12",
    borderColor: "#f97316",
    editorBg: "#fff7ed",
    editorText: "#7c2d12",
    editorFocusBg: "#fed7aa",
    controlsBg: "rgba(249, 115, 22, 0.12)",
    primaryBg: "#f97316",
    primaryText: "#ffffff",
    primaryHover: "#ea580c",
    secondaryBg: "rgba(249, 115, 22, 0.16)",
    secondaryText: "#7c2d12",
    secondaryHover: "rgba(249, 115, 22, 0.28)",
    mutedText: "#9a3412",
    errorText: "#dc2626",
    errorBg: "rgba(220, 38, 38, 0.08)",
    errorBorder: "rgba(220, 38, 38, 0.2)",
    tableHeaderBg: "rgba(249, 115, 22, 0.16)",
    tableHeaderText: "#7c2d12",
    tableHover: "rgba(249, 115, 22, 0.12)",
    syntaxKeyword: "#c2410c",
    syntaxString: "#047857",
    syntaxNumber: "#7c3aed",
    syntaxComment: "#9a3412",
    syntaxSpecial: "#dc2626",
    syntaxIdentifier: "#facc15",
    fontFamily: "Inter, system-ui, sans-serif",
    editorFontFamily: "'JetBrains Mono', monospace",
    fontSize: "14px",
    editorFontSize: "13px",
    buttonFontSize: "13px",
    metadataFontSize: "12px",
  },
});

// Manually mount widgets when needed
document.querySelectorAll("pre.sql-demo").forEach((node) => {
  window.PondPilot.create(node, { theme: "sunset" });
});

Data attributes

Per-snippet overrides can be expressed declaratively:

<pre
  class="pondpilot-snippet"
  data-theme="sunset"
  data-base-url="https://cdn.example.com/data"
  data-init-queries='["LOAD httpfs"]'
>
  <code>SELECT * FROM 'sales.parquet';</code>
</pre>

Supported attributes include:

  • data-theme
  • data-base-url
  • data-editable
  • data-show-powered-by
  • data-init-queries (semicolon or JSON array)

API Summary

  • PondPilot.init(target?, options?) – initialize matching elements (defaults to config.selector)
  • PondPilot.create(element, options?) – mount a single instance on demand
  • PondPilot.destroy(target?) – remove widgets (defaults to all)
  • PondPilot.config(partial) – merge configuration
  • PondPilot.getConfig() – inspect current configuration
  • PondPilot.registerTheme(name, definition) – extend theming system
  • PondPilot.Widget – constructor for manual control (new PondPilot.Widget(element, options))

Vue

<template>
  <pre ref="editor" class="pondpilot-snippet">{{ sql }}</pre>
</template>

<script>
import 'pondpilot-widget';

export default {
  props: ['sql', 'options'],
  mounted() {
    this.widget = new window.PondPilot.Widget(this.$refs.editor, this.options);
  },
  beforeUnmount() {
    this.widget?.destroy();
  }
}
</script>

Markdown/Documentation Sites

Docusaurus

// In docusaurus.config.js
module.exports = {
  scripts: [
    'https://unpkg.com/pondpilot-widget'
  ],
  // ...
};

VitePress

// In .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme';
import 'pondpilot-widget';

export default DefaultTheme;

Local Examples (in this repo)

You can run a small examples site from the examples/ folder:

  1. Serve the repository root (so examples/ is web‑served):
python3 -m http.server 8080
# then open http://localhost:8080/examples/index.html
  1. Local .duckdb database demo (optional):
# create a tiny demo DB at examples/data/blog.duckdb
uv run examples/create-blog-db.py   # or: pip install duckdb pandas && python3 examples/create-blog-db.py

# open the demo page
open http://localhost:8080/examples/local-duckdb.html
  1. Relative Parquet path handling demo (optional):
cd examples/relative-paths
python3 create-test-data.py  # creates analytics.parquet in this directory
cd ../..
open http://localhost:8080/examples/relative-paths/test-relative-paths.html
  1. A5 Geospatial Extension demo with interactive map:
# No setup required - just open the demo
open http://localhost:8080/examples/a5-geospatial-demo.html

Notes:

  • The example pages in this repo load the widget from ../src/ for local development. You can switch them to use the CDN by replacing the script tag with the CDN snippet from Installation.
  • The local .duckdb example shows how to provide an external DuckDB WASM instance to the widget and open a DB file served over HTTP.
  • The relative‑paths example demonstrates how the widget auto‑resolves relative parquet file paths in queries.
  • The A5 demo shows how to use DuckDB community extensions for geospatial analysis with interactive Leaflet map visualization.

Configuration Options

Option Type Default Description
selector string "pre.pondpilot-snippet, .pondpilot-snippet pre" CSS selector for SQL blocks
theme string "light" Theme: "light" or "dark"
editable boolean true Allow editing SQL code
showPoweredBy boolean true Show "Powered by PondPilot" branding
baseUrl string "http://localhost:5173" PondPilot instance URL
autoInit boolean true Auto-initialize on DOM ready

API Reference

Global Object

window.PondPilot = {
  init(),           // Initialize all widgets
  destroy(),        // Clean up all widgets
  config: {},       // Global configuration
  Widget: class {}  // Widget constructor
};

Widget Instance

const widget = new PondPilot.Widget(element, options);

// Methods
await widget.run();      // Execute SQL query
widget.reset();          // Reset to original code
await widget.cleanup();  // Clean up resources
widget.destroy();        // Destroy widget

Security Considerations

  1. Content Security Policy (CSP)

    Content-Security-Policy:
      script-src 'self' https://unpkg.com https://cdn.jsdelivr.net;
      worker-src 'self' blob: https://cdn.jsdelivr.net;
      connect-src 'self' https://cdn.jsdelivr.net;
    
  2. Subresource Integrity (SRI)

    <script
     src="https://unpkg.com/[email protected]"
      integrity="sha384-[hash]"
      crossorigin="anonymous">
    </script>

Browser Support

  • Chrome/Edge 88+
  • Firefox 89+
  • Safari 15+

Requires:

  • WebAssembly
  • Web Workers
  • ES2018+

Working with Parquet Files

The widget supports loading parquet files from various sources:

Direct HTTP URLs

SELECT * FROM 'https://example.com/data.parquet' LIMIT 10;

Relative Paths (New in v1.1.0)

The widget automatically resolves relative paths to absolute URLs:

-- All of these formats are supported:
SELECT * FROM 'data.parquet';
SELECT * FROM './data.parquet';
SELECT * FROM '/data.parquet';

When using relative paths:

  • Files must be accessible via HTTP from the same server
  • The widget resolves paths relative to the current page URL
  • For local development, ensure your files are served by a web server

Performance Tips

  1. Lazy Loading: DuckDB is only loaded when first query runs
  2. Shared Instance: Multiple widgets share the same DuckDB instance
  3. Resource Cleanup: Widgets automatically clean up when removed from DOM
  4. File Caching: Registered parquet files are cached to avoid duplicate downloads

Changelog

See CHANGELOG.md for release history.

License

MIT © PondPilot

Contributing

Contributions welcome! Please read our contributing guide.

Local development

npm install       # install dependencies
npm run format    # apply Prettier formatting
npm test          # run the Vitest suite

Prefer just? We provide a lightweight justfile with the same commands (just format, just test, etc.).

Support

About

Transform static SQL code blocks into interactive snippets powered by DuckDB WASM.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •