File size: 6,703 Bytes
bcf46c3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
import React, { useState, useEffect, useRef } from 'react';
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';
import { initLocalEngine, routeInference } from '../utils/inferenceRouter';
import HCaptcha from '@hcaptcha/react-hcaptcha';
import { supabase, useAuth, GATEWAY_URL, LEMON_CHECKOUT_URL } from '../context';

export default function CodePlayground({ apiKey }) {
  const [tab, setTab] = useState('python')
  const key = apiKey || 'YOUR_API_KEY'
  
  // Local Mode State
  const [isLocalMode, setIsLocalMode] = useState(false)
  const [localProgress, setLocalProgress] = useState('')
  const [isModelLoaded, setIsModelLoaded] = useState(false)
  const [isInitializing, setIsInitializing] = useState(false)
  
  // Interactive Run State
  const [testPrompt, setTestPrompt] = useState('Extract main headings from this text...')
  const [testImage, setTestImage] = useState('') // Optional image URL or base64
  const [result, setResult] = useState('')
  const [isRunning, setIsRunning] = useState(false)

  const snippets = {
    python: `import requests

response = requests.post(
    "https://your-gateway.com/api/vision-scrape",
    headers={"X-API-Key": "${key}"},
    json={
        "target_url": "https://example.com",
        "extraction_query": "Extract all product names and prices as JSON"
    }
)

print(response.json())`,
    node: `const axios = require('axios');

const response = await axios.post(
  'https://your-gateway.com/api/vision-scrape',
  {
    target_url: 'https://example.com',
    extraction_query: 'Extract all product names and prices as JSON'
  },
  {
    headers: { 'X-API-Key': '${key}' }
  }
);

console.log(response.data);`,
    curl: `curl -X POST https://your-gateway.com/api/vision-scrape \\
  -H "Content-Type: application/json" \\
  -H "X-API-Key: ${key}" \\
  -d '{
    "target_url": "https://example.com",
    "extraction_query": "Extract all product names and prices as JSON"
  }'`,
  }

  const copySnippet = () => {
    navigator.clipboard.writeText(snippets[tab])
  }

  const handleToggleLocal = async (e) => {
    const checked = e.target.checked;
    setIsLocalMode(checked);
    if (checked && !isModelLoaded) {
      setIsInitializing(true);
      try {
        await initLocalEngine((progress) => {
          setLocalProgress(progress.text);
        });
        setIsModelLoaded(true);
        setLocalProgress('Model loaded successfully!');
      } catch (err) {
        if (err.message === "WEBGPU_UNSUPPORTED") {
          setLocalProgress('WebGPU unsupported by your browser. Falling back to cloud API...');
        } else {
          setLocalProgress('Failed to load local model.');
          console.error(err);
        }
        setIsLocalMode(false);
      } finally {
        setIsInitializing(false);
      }
    }
  }

  const handleRun = async () => {
    setIsRunning(true);
    setResult('Running...');
    try {
      const payload = testImage ? { prompt: testPrompt, image: testImage } : testPrompt;
      const res = await routeInference(payload, isLocalMode, GATEWAY_URL, key);
      setResult(JSON.stringify(res, null, 2));
    } catch (err) {
      setResult('Error: ' + err.message);
    } finally {
      setIsRunning(false);
    }
  }

  return (
    <div className="card animate-in" style={{ padding: 0, overflow: 'hidden' }}>
      <div style={{ padding: '1.25rem 1.5rem 0', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <div className="card-label">Interactive Playground</div>
        
        <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.85rem' }}>
          <label style={{ cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '0.25rem' }}>
            <input 
              type="checkbox" 
              checked={isLocalMode} 
              onChange={handleToggleLocal}
              disabled={isInitializing}
            />
            Run Locally (Zero-Cost WebLLM)
          </label>
        </div>
      </div>
      
      {isInitializing && (
        <div style={{ padding: '0.5rem 1.5rem', fontSize: '0.8rem', color: 'var(--cyan)' }}>
          Loading Model: {localProgress}
        </div>
      )}
      {isModelLoaded && isLocalMode && (
        <div style={{ padding: '0.5rem 1.5rem', fontSize: '0.8rem', color: 'var(--green)' }}>
          Local Model Ready. Text queries will be processed in-browser.
        </div>
      )}

      {/* Interactive Run Section */}
      <div style={{ padding: '1rem 1.5rem', borderBottom: '1px solid var(--border)' }}>
        <div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
          <input 
            type="text" 
            placeholder="Image URL (optional, triggers Vision Backend)" 
            value={testImage} 
            onChange={e => setTestImage(e.target.value)} 
            style={{ padding: '0.5rem', borderRadius: '4px', border: '1px solid var(--border)', background: 'var(--bg1)', color: 'var(--text)' }}
          />
          <textarea 
            placeholder="Enter prompt..." 
            value={testPrompt} 
            onChange={e => setTestPrompt(e.target.value)}
            style={{ padding: '0.5rem', borderRadius: '4px', border: '1px solid var(--border)', background: 'var(--bg1)', color: 'var(--text)', minHeight: '60px' }}
          />
          <button 
            onClick={handleRun} 
            disabled={isRunning || (isLocalMode && !isModelLoaded)}
            className="btn btn-primary" 
            style={{ width: 'fit-content' }}
          >
            {isRunning ? 'Running...' : 'Run Inference'}
          </button>
          
          {result && (
            <pre style={{ background: '#000', padding: '1rem', borderRadius: '8px', fontSize: '0.8rem', overflowX: 'auto', border: '1px solid var(--border)' }}>
              {result}
            </pre>
          )}
        </div>
      </div>

      {/* Existing Snippets Section */}
      <div style={{ padding: '1.25rem 1.5rem 0' }}>
        <div className="card-label">Integration Snippets</div>
      </div>
      <div style={{ padding: '0 1.5rem' }}>
        <div className="code-tabs">
          {['python', 'node', 'curl'].map(t => (
            <button key={t} className={`code-tab ${tab === t ? 'active' : ''}`} onClick={() => setTab(t)}>
              {t === 'python' ? 'Python' : t === 'node' ? 'Node.js' : 'cURL'}
            </button>
          ))}
        </div>
      </div>
      <div style={{ padding: '0 1.5rem 1.5rem' }}>
        <div className="code-block">
          <button className="code-copy" onClick={copySnippet}>Copy</button>
          {snippets[tab]}
        </div>
      </div>
    </div>
  )
}