znation HF Staff commited on
Commit
04b34c7
·
1 Parent(s): d2e7d62

first prompt vibe coded

Browse files
Files changed (2) hide show
  1. index.html +175 -8
  2. style.css +201 -16
index.html CHANGED
@@ -3,17 +3,184 @@
3
  <head>
4
  <meta charset="utf-8" />
5
  <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
  <link rel="stylesheet" href="style.css" />
 
 
 
8
  </head>
9
  <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  </body>
19
  </html>
 
3
  <head>
4
  <meta charset="utf-8" />
5
  <meta name="viewport" content="width=device-width" />
6
+ <title>Parquet Visualization Studio</title>
7
  <link rel="stylesheet" href="style.css" />
8
+ <script src="https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@latest/dist/duckdb-mvp.wasm.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@latest/dist/duckdb-browser-mvp.worker.js"></script>
10
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@latest/dist/duckdb-browser-mvp.worker.js"></script>
11
  </head>
12
  <body>
13
+ <div class="container">
14
+ <h1>📊 Parquet Visualization Studio</h1>
15
+ <p class="subtitle">Query parquet files using SQL with DuckDB WASM</p>
16
+
17
+ <form id="queryForm">
18
+ <div class="form-group">
19
+ <label for="parquetUrl">Parquet File URL</label>
20
+ <input
21
+ type="text"
22
+ id="parquetUrl"
23
+ placeholder="https://example.com/data.parquet"
24
+ required
25
+ />
26
+ </div>
27
+
28
+ <div class="form-group">
29
+ <label for="sqlQuery">SQL Query</label>
30
+ <textarea
31
+ id="sqlQuery"
32
+ rows="4"
33
+ placeholder="SELECT * FROM data LIMIT 10"
34
+ required
35
+ ></textarea>
36
+ </div>
37
+
38
+ <button type="submit" id="submitBtn">Run Query</button>
39
+ </form>
40
+
41
+ <div id="status" class="status"></div>
42
+
43
+ <div id="resultsContainer" class="results-container" style="display: none;">
44
+ <h2>Results</h2>
45
+ <div id="results" class="results"></div>
46
+ </div>
47
  </div>
48
+
49
+ <script type="module">
50
+ import * as duckdb from 'https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@latest/+esm';
51
+
52
+ let db = null;
53
+ let conn = null;
54
+
55
+ // Initialize DuckDB
56
+ async function initDuckDB() {
57
+ const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();
58
+ const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);
59
+ const worker_url = URL.createObjectURL(
60
+ new Blob([`importScripts("${bundle.mainWorker}");`], { type: 'text/javascript' })
61
+ );
62
+ const worker = new Worker(worker_url);
63
+ const logger = new duckdb.ConsoleLogger();
64
+ db = new duckdb.AsyncDuckDB(logger, worker);
65
+ await db.instantiate(bundle.mainModule, bundle.pthreadWorker);
66
+ URL.revokeObjectURL(worker_url);
67
+ conn = await db.connect();
68
+ }
69
+
70
+ // Update status message
71
+ function setStatus(message, type = 'info') {
72
+ const statusEl = document.getElementById('status');
73
+ statusEl.textContent = message;
74
+ statusEl.className = `status status-${type}`;
75
+ statusEl.style.display = 'block';
76
+ }
77
+
78
+ // Render results as HTML table
79
+ function renderResults(result) {
80
+ const resultsContainer = document.getElementById('resultsContainer');
81
+ const resultsEl = document.getElementById('results');
82
+
83
+ resultsContainer.style.display = 'block';
84
+
85
+ if (!result || result.numRows === 0) {
86
+ resultsEl.innerHTML = '<p class="no-results">No results found.</p>';
87
+ return;
88
+ }
89
+
90
+ // Get column names and rows
91
+ const columns = result.schema.fields.map(field => field.name);
92
+ const rows = result.toArray();
93
+
94
+ // Build HTML table
95
+ let tableHtml = '<div class="table-wrapper"><table><thead><tr>';
96
+
97
+ // Add headers
98
+ columns.forEach(col => {
99
+ tableHtml += `<th>${escapeHtml(col)}</th>`;
100
+ });
101
+ tableHtml += '</tr></thead><tbody>';
102
+
103
+ // Add rows
104
+ rows.forEach(row => {
105
+ tableHtml += '<tr>';
106
+ columns.forEach(col => {
107
+ const value = row[col];
108
+ tableHtml += `<td>${escapeHtml(String(value ?? ''))}</td>`;
109
+ });
110
+ tableHtml += '</tr>';
111
+ });
112
+
113
+ tableHtml += '</tbody></table></div>';
114
+ tableHtml += `<p class="row-count">Showing ${result.numRows} row(s)</p>`;
115
+
116
+ resultsEl.innerHTML = tableHtml;
117
+ }
118
+
119
+ // Escape HTML to prevent XSS
120
+ function escapeHtml(text) {
121
+ const div = document.createElement('div');
122
+ div.textContent = text;
123
+ return div.innerHTML;
124
+ }
125
+
126
+ // Handle form submission
127
+ async function handleSubmit(e) {
128
+ e.preventDefault();
129
+
130
+ const parquetUrl = document.getElementById('parquetUrl').value.trim();
131
+ const sqlQuery = document.getElementById('sqlQuery').value.trim();
132
+ const submitBtn = document.getElementById('submitBtn');
133
+
134
+ if (!parquetUrl || !sqlQuery) {
135
+ setStatus('Please provide both a parquet URL and SQL query.', 'error');
136
+ return;
137
+ }
138
+
139
+ try {
140
+ submitBtn.disabled = true;
141
+ submitBtn.textContent = 'Running...';
142
+ setStatus('Initializing DuckDB...', 'info');
143
+
144
+ // Initialize DuckDB if not already done
145
+ if (!db) {
146
+ await initDuckDB();
147
+ }
148
+
149
+ setStatus('Loading parquet file...', 'info');
150
+
151
+ // Register the parquet file from URL
152
+ await db.registerFileURL(
153
+ 'data.parquet',
154
+ parquetUrl,
155
+ duckdb.DuckDBDataProtocol.HTTP,
156
+ false
157
+ );
158
+
159
+ setStatus('Executing query...', 'info');
160
+
161
+ // Execute the query
162
+ const result = await conn.query(sqlQuery.replace(/\bdata\b/gi, "'data.parquet'"));
163
+
164
+ setStatus('Query completed successfully!', 'success');
165
+
166
+ // Render results
167
+ renderResults(result);
168
+
169
+ } catch (error) {
170
+ console.error('Error:', error);
171
+ setStatus(`Error: ${error.message}`, 'error');
172
+ document.getElementById('resultsContainer').style.display = 'none';
173
+ } finally {
174
+ submitBtn.disabled = false;
175
+ submitBtn.textContent = 'Run Query';
176
+ }
177
+ }
178
+
179
+ // Set up event listeners
180
+ document.getElementById('queryForm').addEventListener('submit', handleSubmit);
181
+
182
+ // Initialize on load
183
+ setStatus('Ready to query parquet files!', 'success');
184
+ </script>
185
  </body>
186
  </html>
style.css CHANGED
@@ -1,28 +1,213 @@
 
 
 
 
 
 
1
  body {
2
  padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
 
 
 
 
 
 
 
 
 
4
  }
5
 
6
  h1 {
7
- font-size: 16px;
8
- margin-top: 0;
 
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  }
 
1
+ * {
2
+ box-sizing: border-box;
3
+ margin: 0;
4
+ padding: 0;
5
+ }
6
+
7
  body {
8
  padding: 2rem;
9
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica", "Arial", sans-serif;
10
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
11
+ min-height: 100vh;
12
+ }
13
+
14
+ .container {
15
+ max-width: 900px;
16
+ margin: 0 auto;
17
+ background: white;
18
+ padding: 2rem;
19
+ border-radius: 12px;
20
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
21
  }
22
 
23
  h1 {
24
+ font-size: 2rem;
25
+ margin-bottom: 0.5rem;
26
+ color: #1a202c;
27
  }
28
 
29
+ .subtitle {
30
+ color: #718096;
31
+ font-size: 1rem;
32
+ margin-bottom: 2rem;
 
33
  }
34
 
35
+ h2 {
36
+ font-size: 1.5rem;
37
+ margin-bottom: 1rem;
38
+ color: #2d3748;
39
+ }
40
+
41
+ .form-group {
42
+ margin-bottom: 1.5rem;
43
+ }
44
+
45
+ label {
46
+ display: block;
47
+ font-weight: 600;
48
+ margin-bottom: 0.5rem;
49
+ color: #2d3748;
50
+ font-size: 0.95rem;
51
+ }
52
+
53
+ input[type="text"],
54
+ textarea {
55
+ width: 100%;
56
+ padding: 0.75rem;
57
+ border: 2px solid #e2e8f0;
58
+ border-radius: 8px;
59
+ font-size: 0.95rem;
60
+ font-family: 'Monaco', 'Courier New', monospace;
61
+ transition: border-color 0.2s, box-shadow 0.2s;
62
+ }
63
+
64
+ input[type="text"]:focus,
65
+ textarea:focus {
66
+ outline: none;
67
+ border-color: #667eea;
68
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
69
  }
70
 
71
+ textarea {
72
+ resize: vertical;
73
+ min-height: 100px;
74
+ }
75
+
76
+ button[type="submit"] {
77
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
78
+ color: white;
79
+ border: none;
80
+ padding: 0.875rem 2rem;
81
+ font-size: 1rem;
82
+ font-weight: 600;
83
+ border-radius: 8px;
84
+ cursor: pointer;
85
+ transition: transform 0.2s, box-shadow 0.2s;
86
+ width: 100%;
87
+ }
88
+
89
+ button[type="submit"]:hover:not(:disabled) {
90
+ transform: translateY(-2px);
91
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
92
+ }
93
+
94
+ button[type="submit"]:active:not(:disabled) {
95
+ transform: translateY(0);
96
+ }
97
+
98
+ button[type="submit"]:disabled {
99
+ opacity: 0.6;
100
+ cursor: not-allowed;
101
+ }
102
+
103
+ .status {
104
+ margin: 1.5rem 0;
105
+ padding: 1rem;
106
+ border-radius: 8px;
107
+ font-size: 0.95rem;
108
+ display: none;
109
+ }
110
+
111
+ .status-info {
112
+ background: #ebf8ff;
113
+ color: #2c5282;
114
+ border-left: 4px solid #4299e1;
115
+ }
116
+
117
+ .status-success {
118
+ background: #f0fff4;
119
+ color: #22543d;
120
+ border-left: 4px solid #48bb78;
121
+ }
122
+
123
+ .status-error {
124
+ background: #fff5f5;
125
+ color: #742a2a;
126
+ border-left: 4px solid #f56565;
127
+ }
128
+
129
+ .results-container {
130
+ margin-top: 2rem;
131
+ padding-top: 2rem;
132
+ border-top: 2px solid #e2e8f0;
133
+ }
134
+
135
+ .results {
136
+ margin-top: 1rem;
137
+ }
138
+
139
+ .table-wrapper {
140
+ overflow-x: auto;
141
+ border-radius: 8px;
142
+ border: 1px solid #e2e8f0;
143
+ }
144
+
145
+ table {
146
+ width: 100%;
147
+ border-collapse: collapse;
148
+ font-size: 0.9rem;
149
+ }
150
+
151
+ th {
152
+ background: #f7fafc;
153
+ padding: 0.75rem;
154
+ text-align: left;
155
+ font-weight: 600;
156
+ color: #2d3748;
157
+ border-bottom: 2px solid #e2e8f0;
158
+ white-space: nowrap;
159
+ }
160
+
161
+ td {
162
+ padding: 0.75rem;
163
+ border-bottom: 1px solid #e2e8f0;
164
+ color: #4a5568;
165
+ }
166
+
167
+ tr:last-child td {
168
+ border-bottom: none;
169
+ }
170
+
171
+ tr:hover {
172
+ background: #f7fafc;
173
+ }
174
+
175
+ .row-count {
176
+ margin-top: 1rem;
177
+ color: #718096;
178
+ font-size: 0.9rem;
179
+ font-style: italic;
180
+ }
181
+
182
+ .no-results {
183
+ color: #718096;
184
+ font-style: italic;
185
+ padding: 2rem;
186
+ text-align: center;
187
+ }
188
+
189
+ @media (max-width: 768px) {
190
+ body {
191
+ padding: 1rem;
192
+ }
193
+
194
+ .container {
195
+ padding: 1.5rem;
196
+ }
197
+
198
+ h1 {
199
+ font-size: 1.5rem;
200
+ }
201
+
202
+ button[type="submit"] {
203
+ padding: 0.75rem 1.5rem;
204
+ }
205
+
206
+ table {
207
+ font-size: 0.8rem;
208
+ }
209
+
210
+ th, td {
211
+ padding: 0.5rem;
212
+ }
213
  }