CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-gradio--wasm

WebAssembly support for Gradio applications enabling Python code execution in the browser through Pyodide integration

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

svelte-integration.mddocs/

Svelte Integration

Svelte framework integration with context management and specialized components for WebAssembly worker integration. Provides seamless integration between Svelte applications and the @gradio/wasm worker system.

Capabilities

Context Management

Svelte context functions for sharing WorkerProxy instances across components.

/**
 * Set worker proxy in Svelte context for sharing across components
 * @param workerProxy - WorkerProxy instance to share
 */
function setWorkerProxyContext(workerProxy: WorkerProxy): void;

/**
 * Get worker proxy from Svelte context
 * @returns WorkerProxy instance or undefined if not set
 */
function getWorkerProxyContext(): WorkerProxy | undefined;

Usage Examples:

// App.svelte - Root component
<script lang="ts">
  import { onMount } from 'svelte';
  import { WorkerProxy } from '@gradio/wasm';
  import { setWorkerProxyContext } from '@gradio/wasm/svelte';
  import PythonExecutor from './PythonExecutor.svelte';
  import DataAnalyzer from './DataAnalyzer.svelte';

  let worker: WorkerProxy;
  let workerReady = false;

  onMount(async () => {
    // Create and initialize worker
    worker = new WorkerProxy({
      gradioWheelUrl: 'https://example.com/gradio.whl',
      gradioClientWheelUrl: 'https://example.com/gradio_client.whl',
      files: {},
      requirements: ['numpy', 'pandas', 'matplotlib'],
      sharedWorkerMode: false
    });

    // Set worker in context for child components
    setWorkerProxyContext(worker);

    worker.addEventListener('initialization-completed', () => {
      workerReady = true;
    });
  });
</script>

{#if workerReady}
  <div class="app">
    <h1>Python-powered Svelte App</h1>
    <PythonExecutor />
    <DataAnalyzer />
  </div>
{:else}
  <div class="loading">Initializing Python environment...</div>
{/if}
// PythonExecutor.svelte - Child component using context
<script lang="ts">
  import { getWorkerProxyContext } from '@gradio/wasm/svelte';
  
  const worker = getWorkerProxyContext();
  
  let code = 'print("Hello from Python!")';
  let output = '';
  let isExecuting = false;

  async function executeCode() {
    if (!worker) {
      console.error('Worker not available in context');
      return;
    }

    isExecuting = true;
    output = '';

    try {
      // Listen for output
      const handleStdout = (event: CustomEvent) => {
        output += event.detail.data + '\n';
      };

      worker.addEventListener('stdout', handleStdout);
      
      await worker.runPythonCode(code);
      
      worker.removeEventListener('stdout', handleStdout);
    } catch (error) {
      output += `Error: ${error}\n`;
    } finally {
      isExecuting = false;
    }
  }
</script>

<div class="python-executor">
  <h2>Python Code Executor</h2>
  
  <textarea 
    bind:value={code} 
    placeholder="Enter Python code..."
    disabled={isExecuting}
  ></textarea>
  
  <button on:click={executeCode} disabled={isExecuting || !worker}>
    {isExecuting ? 'Executing...' : 'Run Code'}
  </button>
  
  {#if output}
    <pre class="output">{output}</pre>
  {/if}
</div>

<style>
  .python-executor {
    margin: 20px 0;
    padding: 20px;
    border: 1px solid #ddd;
    border-radius: 8px;
  }
  
  textarea {
    width: 100%;
    height: 150px;
    font-family: monospace;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
  }
  
  button {
    margin-top: 10px;
    padding: 10px 20px;
    background: #007bff;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  }
  
  button:disabled {
    background: #ccc;
    cursor: not-allowed;
  }
  
  .output {
    margin-top: 10px;
    padding: 10px;
    background: #f8f9fa;
    border: 1px solid #e9ecef;
    border-radius: 4px;
    font-family: monospace;
    white-space: pre-wrap;
  }
</style>

File URL Resolution

Functions for handling file URLs through the WebAssembly worker proxy.

/**
 * Check if source URL should be proxied through WASM worker
 * @param src - Source URL string to check
 * @returns true if URL should be proxied
 */
function should_proxy_wasm_src(src: string | undefined | null): boolean;

/**
 * Resolve source URL through WASM worker proxy
 * @param src - Source URL to resolve
 * @returns Promise resolving to proxied URL or original URL
 */
function resolve_wasm_src(src: string | undefined | null): Promise<string | undefined | null>;

Usage Examples:

// FileDownloader.svelte
<script lang="ts">
  import { getWorkerProxyContext } from '@gradio/wasm/svelte';
  import { should_proxy_wasm_src, resolve_wasm_src } from '@gradio/wasm/svelte';
  
  export let src: string;
  export let filename: string;
  
  const worker = getWorkerProxyContext();
  
  let resolvedSrc: string | null = null;
  let isResolving = false;
  
  $: if (src) {
    resolveSource(src);
  }
  
  async function resolveSource(source: string) {
    isResolving = true;
    
    try {
      if (should_proxy_wasm_src(source)) {
        console.log('Proxying URL through WASM worker:', source);
        resolvedSrc = await resolve_wasm_src(source);
      } else {
        console.log('Using direct URL:', source);
        resolvedSrc = source;
      }
    } catch (error) {
      console.error('Failed to resolve source:', error);
      resolvedSrc = source; // Fallback to original
    } finally {
      isResolving = false;
    }
  }
</script>

{#if isResolving}
  <div class="resolving">Resolving file URL...</div>
{:else if resolvedSrc}
  <a href={resolvedSrc} download={filename} class="download-link">
    Download {filename}
  </a>
{:else}
  <div class="error">Failed to resolve file URL</div>
{/if}

<style>
  .download-link {
    display: inline-block;
    padding: 8px 16px;
    background: #28a745;
    color: white;
    text-decoration: none;
    border-radius: 4px;
    font-weight: bold;
  }
  
  .download-link:hover {
    background: #218838;
  }
  
  .resolving, .error {
    padding: 8px 16px;
    border-radius: 4px;
  }
  
  .resolving {
    background: #fff3cd;
    color: #856404;
  }
  
  .error {
    background: #f8d7da;
    color: #721c24;
  }
</style>

DownloadLink Component

Specialized Svelte component for download links that work with the WASM worker.

/**
 * Download link component that works with WASM worker
 * Props:
 * - href: string | undefined - Download URL
 * - download: string - Download filename  
 * - ...$$restProps - Additional anchor attributes
 * 
 * Events:
 * - click - Dispatched on click
 * 
 * Slots:
 * - default - Link content
 */
// DownloadLink.svelte component interface

Usage Examples:

// Using DownloadLink component
<script lang="ts">
  import { DownloadLink } from '@gradio/wasm/svelte';
  import { getWorkerProxyContext } from '@gradio/wasm/svelte';
  
  const worker = getWorkerProxyContext();
  
  let fileUrl: string | undefined;
  let fileName = 'output.csv';
  
  async function generateFile() {
    if (!worker) return;
    
    // Generate data in Python
    await worker.runPythonCode(`
import pandas as pd
import json

# Create sample data
data = {
    'name': ['Alice', 'Bob', 'Charlie'],
    'age': [25, 30, 35],
    'city': ['New York', 'San Francisco', 'Boston']
}

df = pd.DataFrame(data)

# Save to CSV
df.to_csv('output.csv', index=False)
print('File generated: output.csv')
`);
    
    // In a real implementation, you'd get the file URL from the worker
    fileUrl = '/worker-files/output.csv';
  }
  
  function handleDownloadClick(event) {
    console.log('Download initiated:', event.detail);
  }
</script>

<div class="file-generator">
  <h3>Data File Generator</h3>
  
  <button on:click={generateFile} disabled={!worker}>
    Generate CSV File
  </button>
  
  {#if fileUrl}
    <div class="download-section">
      <p>File generated successfully!</p>
      <DownloadLink 
        href={fileUrl} 
        download={fileName}
        class="download-btn"
        on:click={handleDownloadClick}
      >
        📄 Download {fileName}
      </DownloadLink>
    </div>
  {/if}
</div>

<style>
  .file-generator {
    padding: 20px;
    border: 1px solid #ddd;
    border-radius: 8px;
    margin: 20px 0;
  }
  
  button {
    padding: 10px 20px;
    background: #007bff;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  }
  
  button:disabled {
    background: #ccc;
    cursor: not-allowed;
  }
  
  .download-section {
    margin-top: 15px;
    padding: 15px;
    background: #d4edda;
    border: 1px solid #c3e6cb;
    border-radius: 4px;
  }
  
  :global(.download-btn) {
    display: inline-block;
    padding: 10px 20px;
    background: #28a745;
    color: white;
    text-decoration: none;
    border-radius: 4px;
    font-weight: bold;
    margin-top: 10px;
  }
  
  :global(.download-btn:hover) {
    background: #218838;
  }
</style>

Complete Svelte App Example

Full example of a Svelte application integrated with @gradio/wasm:

// main.js - Application entry point
import App from './App.svelte';

const app = new App({
  target: document.body,
  props: {
    name: 'Python-Powered Svelte App'
  }
});

export default app;
// App.svelte - Main application component
<script lang="ts">
  import { onMount, onDestroy } from 'svelte';
  import { WorkerProxy } from '@gradio/wasm';
  import { setWorkerProxyContext } from '@gradio/wasm/svelte';
  
  import Header from './components/Header.svelte';
  import PythonConsole from './components/PythonConsole.svelte';
  import DataVisualizer from './components/DataVisualizer.svelte';
  import FileManager from './components/FileManager.svelte';
  
  export let name: string;
  
  let worker: WorkerProxy;
  let workerStatus: 'initializing' | 'ready' | 'error' = 'initializing';
  let initializationError: string | null = null;

  onMount(async () => {
    try {
      worker = new WorkerProxy({
        gradioWheelUrl: 'https://cdn.jsdelivr.net/pyodide/gradio.whl',
        gradioClientWheelUrl: 'https://cdn.jsdelivr.net/pyodide/gradio_client.whl',
        files: {
          'sample_data.csv': {
            data: 'name,age,score\nAlice,25,95\nBob,30,87\nCharlie,35,92',
            opts: { encoding: 'utf8' }
          }
        },
        requirements: [
          'numpy>=1.21.0',
          'pandas>=1.3.0',
          'matplotlib>=3.4.0',
          'seaborn>=0.11.0'
        ],
        sharedWorkerMode: false
      });

      // Set worker in context
      setWorkerProxyContext(worker);

      worker.addEventListener('initialization-completed', () => {
        workerStatus = 'ready';
        console.log('Python environment ready!');
      });

      worker.addEventListener('initialization-error', (event) => {
        workerStatus = 'error';
        initializationError = event.detail?.error || 'Unknown initialization error';
        console.error('Worker initialization failed:', initializationError);
      });

      worker.addEventListener('python-error', (event) => {
        console.error('Python error:', event.detail);
      });

    } catch (error) {
      workerStatus = 'error';
      initializationError = error.toString();
      console.error('Failed to create worker:', error);
    }
  });

  onDestroy(() => {
    if (worker) {
      worker.terminate();
    }
  });
</script>

<main>
  <Header {name} status={workerStatus} />
  
  {#if workerStatus === 'initializing'}
    <div class="status-panel initializing">
      <div class="spinner"></div>
      <p>Initializing Python environment...</p>
      <small>This may take a minute on first load</small>
    </div>
  {:else if workerStatus === 'error'}
    <div class="status-panel error">
      <h3>❌ Initialization Failed</h3>
      <p>{initializationError}</p>
      <button on:click={() => location.reload()}>
        🔄 Retry
      </button>
    </div>
  {:else if workerStatus === 'ready'}
    <div class="app-content">
      <div class="left-panel">
        <PythonConsole />
        <FileManager />
      </div>
      <div class="right-panel">
        <DataVisualizer />
      </div>
    </div>
  {/if}
</main>

<style>
  main {
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  }
  
  .status-panel {
    text-align: center;
    padding: 40px;
    border-radius: 8px;
    margin: 40px 0;
  }
  
  .status-panel.initializing {
    background: #fff3cd;
    border: 1px solid #ffeaa7;
    color: #856404;
  }
  
  .status-panel.error {
    background: #f8d7da;
    border: 1px solid #f5c6cb;
    color: #721c24;
  }
  
  .spinner {
    width: 40px;
    height: 40px;
    border: 4px solid #f3f3f3;
    border-top: 4px solid #007bff;
    border-radius: 50%;
    animation: spin 1s linear infinite;
    margin: 0 auto 20px;
  }
  
  @keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
  }
  
  .app-content {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 20px;
    margin-top: 20px;
  }
  
  .left-panel, .right-panel {
    display: flex;
    flex-direction: column;
    gap: 20px;
  }
  
  @media (max-width: 768px) {
    .app-content {
      grid-template-columns: 1fr;
    }
  }
</style>

Integration Best Practices

Key patterns and best practices for Svelte integration:

// WorkerStore.js - Reactive store for worker state
import { writable, derived } from 'svelte/store';
import { WorkerProxy } from '@gradio/wasm';

export const workerStore = writable(null);
export const workerStatus = writable('initializing');
export const pythonOutput = writable([]);

export const isWorkerReady = derived(
  workerStatus, 
  $status => $status === 'ready'
);

export async function initializeWorker(options) {
  const worker = new WorkerProxy(options);
  
  worker.addEventListener('initialization-completed', () => {
    workerStatus.set('ready');
  });
  
  worker.addEventListener('stdout', (event) => {
    pythonOutput.update(output => [...output, { type: 'stdout', data: event.detail.data }]);
  });
  
  worker.addEventListener('stderr', (event) => {
    pythonOutput.update(output => [...output, { type: 'stderr', data: event.detail.data }]);
  });
  
  workerStore.set(worker);
  return worker;
}
// Component using the store
<script lang="ts">
  import { workerStore, isWorkerReady, pythonOutput } from '../stores/WorkerStore.js';
  
  $: worker = $workerStore;
  $: ready = $isWorkerReady;
  $: output = $pythonOutput;
  
  async function runCode(code) {
    if (worker) {
      await worker.runPythonCode(code);
    }
  }
</script>

{#if ready}
  <button on:click={() => runCode('print("Hello from Svelte!")')}>
    Run Python Code
  </button>
  
  <div class="output">
    {#each output as line}
      <div class="output-line {line.type}">
        {line.data}
      </div>
    {/each}
  </div>
{:else}
  <div>Worker not ready</div>
{/if}

docs

code-completion.md

event-streaming.md

file-system.md

http-client.md

index.md

network-utilities.md

package-management.md

svelte-integration.md

worker-management.md

tile.json