@mcp-b/interactive-components 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +99 -0
  3. package/dist/code-editor-B94tewLC.js +425 -0
  4. package/dist/code-preview-gKk0nBKP.js +269 -0
  5. package/dist/components/code-editor/code-editor.d.ts +65 -0
  6. package/dist/components/code-editor/code-editor.js +2 -0
  7. package/dist/components/code-preview/code-preview.d.ts +58 -0
  8. package/dist/components/code-preview/code-preview.js +3 -0
  9. package/dist/components/interactive-editor/interactive-editor.d.ts +77 -0
  10. package/dist/components/interactive-editor/interactive-editor.js +3 -0
  11. package/dist/components/r-editor/r-editor.d.ts +68 -0
  12. package/dist/components/r-editor/r-editor.js +2 -0
  13. package/dist/components/sql-editor/sql-editor.d.ts +78 -0
  14. package/dist/components/sql-editor/sql-editor.js +2 -0
  15. package/dist/decorate-DcF3lt7P.js +9 -0
  16. package/dist/docs/custom-elements.json +2807 -0
  17. package/dist/index.d.ts +8 -0
  18. package/dist/index.js +8 -0
  19. package/dist/interactive-editor-WfTWxJGF.js +1454 -0
  20. package/dist/lib/runner-channel.d.ts +25 -0
  21. package/dist/lib/runner-channel.js +55 -0
  22. package/dist/lib/runner-url.d.ts +15 -0
  23. package/dist/lib/runner-url.js +19 -0
  24. package/dist/lib/transform.d.ts +2 -0
  25. package/dist/lib/transform.js +210 -0
  26. package/dist/r-editor-DwSyDo2i.js +743 -0
  27. package/dist/runners/ephemeral-indexeddb.js +114 -0
  28. package/dist/runners/pglite-runner.js +175 -0
  29. package/dist/runners/pyscript-runner.html +189 -0
  30. package/dist/runners/webr-runner.html +282 -0
  31. package/dist/sql-editor-CsNrjJ2_.js +968 -0
  32. package/dist/themes/interactive.css +79 -0
  33. package/dist/transform-DWhHlnkw.d.ts +53 -0
  34. package/dist/vite.d.ts +14 -0
  35. package/dist/vite.js +62 -0
  36. package/package.json +96 -0
@@ -0,0 +1,968 @@
1
+ import "./code-editor-B94tewLC.js";
2
+ import { t as __decorate } from "./decorate-DcF3lt7P.js";
3
+ import { isRecord } from "./lib/runner-channel.js";
4
+ import { getRunnerUrl } from "./lib/runner-url.js";
5
+ import { css, html, nothing } from "lit";
6
+ import { property, state } from "lit/decorators.js";
7
+ import { SigveloElement } from "@mcp-b/wc-support/base/sigvelo-element";
8
+ import hostStyles from "@mcp-b/wc-support/styles/host.styles";
9
+ import "@mcp-b/web-components/components/spinner/spinner.js";
10
+ import { ifDefined } from "lit/directives/if-defined.js";
11
+ import "@mcp-b/web-components/components/splitter/splitter.js";
12
+ import "@mcp-b/web-components/components/button/button.js";
13
+ import "@mcp-b/web-components/components/button-group/button-group.js";
14
+ import "@mcp-b/web-components/components/tooltip/tooltip.js";
15
+ //#region src/components/sql-editor/sql-editor.styles.ts
16
+ var sql_editor_styles_default = css`
17
+ /* ═══════════════════════════════════════════
18
+ HOST
19
+ ═══════════════════════════════════════════ */
20
+
21
+ :host {
22
+ display: flex;
23
+ flex-direction: column;
24
+ height: var(--sql-editor-height, 560px);
25
+ border: 1px solid var(--sigvelo-color-neutral-border-muted, #d1d5db);
26
+ border-radius: 8px;
27
+ overflow: hidden;
28
+ background: var(--sigvelo-color-surface, #fff);
29
+ font-family:
30
+ system-ui,
31
+ -apple-system,
32
+ sans-serif;
33
+ font-size: 13px;
34
+ color: var(--sigvelo-color-text, #1f2937);
35
+ position: relative;
36
+ }
37
+
38
+ /* ═══════════════════════════════════════════
39
+ CONTENT (desktop layout)
40
+ ═══════════════════════════════════════════ */
41
+
42
+ .content {
43
+ display: flex;
44
+ flex: 1;
45
+ min-height: 0;
46
+ }
47
+
48
+ .content sigvelo-splitter {
49
+ flex: 1;
50
+ }
51
+
52
+ /* ═══════════════════════════════════════════
53
+ EDITOR PANE (left side)
54
+ ═══════════════════════════════════════════ */
55
+
56
+ .editor-pane {
57
+ display: flex;
58
+ flex-direction: column;
59
+ min-width: 0;
60
+ height: 100%;
61
+ }
62
+
63
+ .editor-pane__toolbar {
64
+ display: flex;
65
+ align-items: center;
66
+ gap: 6px;
67
+ padding: 4px 8px;
68
+ border-bottom: 1px solid var(--sigvelo-color-neutral-border-subtle, #e5e7eb);
69
+ background: var(--sigvelo-color-neutral-bg-subtle, #f9fafb);
70
+ min-height: 32px;
71
+ }
72
+
73
+ .editor-pane__toolbar-title {
74
+ font-weight: 600;
75
+ font-size: 12px;
76
+ color: var(--sigvelo-color-neutral-text, #6b7280);
77
+ margin-right: auto;
78
+ }
79
+
80
+ .editor-pane__code {
81
+ flex: 1;
82
+ min-height: 0;
83
+ overflow: hidden;
84
+ }
85
+
86
+ .editor-pane__code sigvelo-code-editor {
87
+ display: block;
88
+ height: 100%;
89
+ }
90
+
91
+ /* ═══════════════════════════════════════════
92
+ RIGHT PANE (results + schema)
93
+ ═══════════════════════════════════════════ */
94
+
95
+ .right-pane {
96
+ display: flex;
97
+ flex-direction: column;
98
+ min-width: 0;
99
+ height: 100%;
100
+ }
101
+
102
+ .right-pane__header {
103
+ display: flex;
104
+ align-items: center;
105
+ gap: 6px;
106
+ padding: 4px 8px;
107
+ border-bottom: 1px solid var(--sigvelo-color-neutral-border-subtle, #e5e7eb);
108
+ background: var(--sigvelo-color-neutral-bg-subtle, #f9fafb);
109
+ min-height: 32px;
110
+ }
111
+
112
+ .right-pane__header sigvelo-button-group {
113
+ margin-right: auto;
114
+ }
115
+
116
+ .right-pane__content {
117
+ flex: 1;
118
+ min-height: 0;
119
+ overflow: auto;
120
+ }
121
+
122
+ /* ═══════════════════════════════════════════
123
+ RESULTS TABLE
124
+ ═══════════════════════════════════════════ */
125
+
126
+ .results-empty {
127
+ display: flex;
128
+ align-items: center;
129
+ justify-content: center;
130
+ height: 100%;
131
+ color: var(--sigvelo-color-neutral-text, #9ca3af);
132
+ font-size: 13px;
133
+ padding: 24px;
134
+ text-align: center;
135
+ }
136
+
137
+ .results-wrapper {
138
+ display: flex;
139
+ flex-direction: column;
140
+ height: 100%;
141
+ }
142
+
143
+ .results-scroll {
144
+ flex: 1;
145
+ overflow: auto;
146
+ min-height: 0;
147
+ }
148
+
149
+ .results-table {
150
+ width: 100%;
151
+ border-collapse: collapse;
152
+ font-family: "SF Mono", "Cascadia Code", "Fira Code", "Consolas", monospace;
153
+ font-size: 12px;
154
+ line-height: 1.5;
155
+ }
156
+
157
+ .results-table thead {
158
+ position: sticky;
159
+ top: 0;
160
+ z-index: 1;
161
+ }
162
+
163
+ .results-table th {
164
+ background: var(--sigvelo-color-neutral-bg-subtle, #f9fafb);
165
+ border-bottom: 1px solid var(--sigvelo-color-neutral-border-subtle, #e5e7eb);
166
+ padding: 0;
167
+ text-align: left;
168
+ font-weight: 600;
169
+ font-size: 11px;
170
+ color: var(--sigvelo-color-neutral-text, #6b7280);
171
+ white-space: nowrap;
172
+ user-select: none;
173
+ }
174
+
175
+ .results-table th:has(.results-table__sort:hover) {
176
+ background: var(--sigvelo-color-neutral-bg-muted, #f3f4f6);
177
+ }
178
+
179
+ .results-table__sort {
180
+ display: block;
181
+ inline-size: 100%;
182
+ min-block-size: 24px;
183
+ padding: 4px 10px;
184
+ border: 0;
185
+ background: transparent;
186
+ color: inherit;
187
+ font: inherit;
188
+ text-align: inherit;
189
+ cursor: pointer;
190
+ }
191
+
192
+ .results-table__sort:focus-visible,
193
+ .schema-table__disclosure:focus-visible,
194
+ .schema-table__action:focus-visible {
195
+ outline: var(--sigvelo-focus-ring);
196
+ outline-offset: calc(-1 * var(--sigvelo-focus-offset));
197
+ }
198
+
199
+ .results-table__sort .sort-indicator {
200
+ margin-left: 4px;
201
+ opacity: 0.5;
202
+ font-size: 10px;
203
+ }
204
+
205
+ .results-table td {
206
+ padding: 3px 10px;
207
+ border-bottom: 1px solid var(--sigvelo-color-neutral-border-subtle, #e5e7eb);
208
+ white-space: pre-wrap;
209
+ word-break: break-word;
210
+ max-width: 300px;
211
+ overflow: hidden;
212
+ text-overflow: ellipsis;
213
+ }
214
+
215
+ .results-table tr:hover td {
216
+ background: var(--sigvelo-color-neutral-bg-subtle, #f9fafb);
217
+ }
218
+
219
+ .results-table .cell--number {
220
+ text-align: right;
221
+ font-variant-numeric: tabular-nums;
222
+ }
223
+
224
+ .results-table .cell--null {
225
+ color: var(--sigvelo-color-neutral-text, #9ca3af);
226
+ font-style: italic;
227
+ }
228
+
229
+ .results-status {
230
+ display: flex;
231
+ align-items: center;
232
+ gap: 12px;
233
+ padding: 4px 10px;
234
+ border-top: 1px solid var(--sigvelo-color-neutral-border-subtle, #e5e7eb);
235
+ background: var(--sigvelo-color-neutral-bg-subtle, #f9fafb);
236
+ font-size: 11px;
237
+ color: var(--sigvelo-color-neutral-text, #6b7280);
238
+ min-height: 24px;
239
+ flex-shrink: 0;
240
+ }
241
+
242
+ /* ═══════════════════════════════════════════
243
+ SCHEMA BROWSER
244
+ ═══════════════════════════════════════════ */
245
+
246
+ .schema-empty {
247
+ color: var(--sigvelo-color-neutral-text, #9ca3af);
248
+ font-size: 13px;
249
+ padding: 24px;
250
+ text-align: center;
251
+ }
252
+
253
+ .schema-list {
254
+ padding: 4px 0;
255
+ }
256
+
257
+ .schema-table {
258
+ padding: 0 8px;
259
+ }
260
+
261
+ .schema-table__header {
262
+ display: flex;
263
+ align-items: center;
264
+ gap: 4px;
265
+ padding: 0;
266
+ border-radius: 4px;
267
+ font-weight: 600;
268
+ font-size: 12px;
269
+ color: var(--sigvelo-color-text, #1f2937);
270
+ user-select: none;
271
+ }
272
+
273
+ .schema-table__header:hover,
274
+ .schema-table__header:focus-within {
275
+ background: var(--sigvelo-color-neutral-bg-subtle, #f9fafb);
276
+ }
277
+
278
+ .schema-table__disclosure {
279
+ display: flex;
280
+ align-items: center;
281
+ flex: 1;
282
+ gap: 4px;
283
+ min-inline-size: 0;
284
+ min-block-size: 24px;
285
+ padding: 4px;
286
+ border: 0;
287
+ border-radius: inherit;
288
+ background: transparent;
289
+ color: inherit;
290
+ font: inherit;
291
+ text-align: left;
292
+ cursor: pointer;
293
+ }
294
+
295
+ .schema-table__chevron {
296
+ width: 12px;
297
+ height: 12px;
298
+ display: flex;
299
+ align-items: center;
300
+ justify-content: center;
301
+ transition: transform 0.15s;
302
+ color: var(--sigvelo-color-neutral-text, #9ca3af);
303
+ }
304
+
305
+ .schema-table__chevron--open {
306
+ transform: rotate(90deg);
307
+ }
308
+
309
+ .schema-table__name {
310
+ flex: 1;
311
+ min-width: 0;
312
+ overflow: hidden;
313
+ text-overflow: ellipsis;
314
+ white-space: nowrap;
315
+ }
316
+
317
+ .schema-table__action {
318
+ opacity: 0;
319
+ min-block-size: 24px;
320
+ border: 0;
321
+ border-radius: var(--sigvelo-radius-xs);
322
+ background: transparent;
323
+ font-size: 10px;
324
+ color: var(--sigvelo-color-primary-text, #2563eb);
325
+ cursor: pointer;
326
+ padding: 0 4px;
327
+ }
328
+
329
+ .schema-table__header:hover .schema-table__action,
330
+ .schema-table__header:focus-within .schema-table__action {
331
+ opacity: 1;
332
+ }
333
+
334
+ .schema-columns {
335
+ padding-left: 20px;
336
+ }
337
+
338
+ .schema-column {
339
+ display: flex;
340
+ align-items: center;
341
+ gap: 6px;
342
+ padding: 2px 4px;
343
+ font-size: 12px;
344
+ color: var(--sigvelo-color-neutral-text, #6b7280);
345
+ }
346
+
347
+ .schema-column__name {
348
+ font-family: "SF Mono", "Cascadia Code", "Fira Code", "Consolas", monospace;
349
+ font-size: 11px;
350
+ color: var(--sigvelo-color-text, #1f2937);
351
+ }
352
+
353
+ .schema-column__type {
354
+ font-size: 10px;
355
+ padding: 0 4px;
356
+ border-radius: 3px;
357
+ background: var(--sigvelo-color-neutral-bg-muted, #f3f4f6);
358
+ color: var(--sigvelo-color-neutral-text, #6b7280);
359
+ white-space: nowrap;
360
+ }
361
+
362
+ /* ═══════════════════════════════════════════
363
+ ERROR DISPLAY
364
+ ═══════════════════════════════════════════ */
365
+
366
+ .error-view {
367
+ padding: 12px;
368
+ font-family: "SF Mono", "Cascadia Code", "Fira Code", "Consolas", monospace;
369
+ font-size: 12px;
370
+ line-height: 1.5;
371
+ color: var(--sigvelo-color-danger-text, #dc2626);
372
+ white-space: pre-wrap;
373
+ word-break: break-word;
374
+ }
375
+
376
+ /* ═══════════════════════════════════════════
377
+ LOADING OVERLAY
378
+ ═══════════════════════════════════════════ */
379
+
380
+ .loading-overlay {
381
+ position: absolute;
382
+ inset: 0;
383
+ display: flex;
384
+ flex-direction: column;
385
+ align-items: center;
386
+ justify-content: center;
387
+ gap: 12px;
388
+ background: color-mix(in srgb, var(--sigvelo-color-surface, #fff) 85%, transparent);
389
+ z-index: 10;
390
+ font-size: 13px;
391
+ color: var(--sigvelo-color-neutral-text, #6b7280);
392
+ }
393
+
394
+ .loading-overlay__message {
395
+ max-width: 280px;
396
+ text-align: center;
397
+ line-height: 1.4;
398
+ }
399
+
400
+ /* ═══════════════════════════════════════════
401
+ MOBILE / COMPACT LAYOUT
402
+ ═══════════════════════════════════════════ */
403
+
404
+ .mobile-nav {
405
+ display: flex;
406
+ align-items: center;
407
+ gap: 6px;
408
+ padding: 4px 8px;
409
+ border-bottom: 1px solid var(--sigvelo-color-neutral-border-subtle, #e5e7eb);
410
+ background: var(--sigvelo-color-neutral-bg-subtle, #f9fafb);
411
+ }
412
+
413
+ .mobile-nav sigvelo-button-group {
414
+ margin-right: auto;
415
+ }
416
+
417
+ .single-pane {
418
+ flex: 1;
419
+ min-height: 0;
420
+ display: flex;
421
+ flex-direction: column;
422
+ }
423
+
424
+ .single-pane .editor-pane {
425
+ flex: 1;
426
+ }
427
+
428
+ .single-pane .results-wrapper {
429
+ flex: 1;
430
+ }
431
+
432
+ .single-pane .schema-list {
433
+ flex: 1;
434
+ }
435
+ `;
436
+ //#endregion
437
+ //#region src/components/sql-editor/sql-editor.ts
438
+ const ICON_PLAY = html`
439
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor" style="display: block">
440
+ <path d="M5 3l14 9-14 9V3z" />
441
+ </svg>
442
+ `;
443
+ const ICON_RESET = html`
444
+ <svg
445
+ width="12"
446
+ height="12"
447
+ viewBox="0 0 24 24"
448
+ fill="none"
449
+ stroke="currentColor"
450
+ stroke-width="2"
451
+ stroke-linecap="round"
452
+ stroke-linejoin="round"
453
+ style="display: block"
454
+ >
455
+ <path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
456
+ <path d="M3 3v5h5" />
457
+ </svg>
458
+ `;
459
+ const ICON_CHEVRON = html`
460
+ <svg
461
+ width="10"
462
+ height="10"
463
+ viewBox="0 0 24 24"
464
+ fill="none"
465
+ stroke="currentColor"
466
+ stroke-width="2.5"
467
+ stroke-linecap="round"
468
+ stroke-linejoin="round"
469
+ style="display: block"
470
+ >
471
+ <path d="M9 18l6-6-6-6" />
472
+ </svg>
473
+ `;
474
+ const COMPACT_BREAKPOINT = 560;
475
+ const MAX_DISPLAY_ROWS = 1e3;
476
+ /**
477
+ * Renders one result cell as text.
478
+ *
479
+ * Postgres returns `json`, `jsonb`, and array columns as objects, which
480
+ * `String()` would render as `[object Object]` in both the table and the sort
481
+ * comparator. Serialising them keeps the value legible and the ordering stable.
482
+ */
483
+ function formatCell(value) {
484
+ if (value === null || value === void 0) return "";
485
+ if (typeof value === "string") return value;
486
+ if (typeof value === "object") return JSON.stringify(value) ?? "";
487
+ return typeof value === "symbol" ? value.toString() : `${value}`;
488
+ }
489
+ /**
490
+ * `<sigvelo-sql-editor>`
491
+ *
492
+ * @summary An in-browser PostgreSQL editor powered by PGlite (WASM). Runs
493
+ * SQL queries locally with no server. Includes a results table, schema
494
+ * browser with autocompletion, and optional dataset preloading via the
495
+ * `setup-sql` attribute.
496
+ *
497
+ * @tag sigvelo-sql-editor
498
+ * @status experimental
499
+ * @since 0.1
500
+ */
501
+ var SigveloSqlEditor = class extends SigveloElement {
502
+ constructor(..._args) {
503
+ super(..._args);
504
+ this.setupSql = "";
505
+ this.code = "";
506
+ this.dbName = "";
507
+ this.postgis = false;
508
+ this.editorLabel = "SQL code editor";
509
+ this._editedCode = "";
510
+ this._pgliteReady = false;
511
+ this._pgliteLoading = true;
512
+ this._loadingMessage = "Initializing PostgreSQL...";
513
+ this._running = false;
514
+ this._resultFields = [];
515
+ this._resultRows = [];
516
+ this._rowCount = 0;
517
+ this._executionTimeMs = 0;
518
+ this._error = null;
519
+ this._schema = [];
520
+ this._expandedTables = /* @__PURE__ */ new Set();
521
+ this._sortColumn = null;
522
+ this._sortDirection = null;
523
+ this._compact = false;
524
+ this._compactView = "code";
525
+ this._rightPane = "results";
526
+ this._runner = null;
527
+ this._resizeObserver = null;
528
+ this._execId = 0;
529
+ this._boundOnKeydown = this._onKeydown.bind(this);
530
+ }
531
+ static {
532
+ this.styles = [hostStyles, sql_editor_styles_default];
533
+ }
534
+ connectedCallback() {
535
+ super.connectedCallback();
536
+ this._editedCode = this.code;
537
+ this._resizeObserver = new ResizeObserver((entries) => {
538
+ for (const entry of entries) {
539
+ const compact = entry.contentRect.width < COMPACT_BREAKPOINT;
540
+ if (compact !== this._compact) this._compact = compact;
541
+ }
542
+ });
543
+ this._resizeObserver.observe(this);
544
+ this.addEventListener("keydown", this._boundOnKeydown);
545
+ }
546
+ firstUpdated() {
547
+ this._initRunner();
548
+ }
549
+ disconnectedCallback() {
550
+ super.disconnectedCallback();
551
+ this._resizeObserver?.disconnect();
552
+ this.removeEventListener("keydown", this._boundOnKeydown);
553
+ this._runner?.terminate();
554
+ this._runner = null;
555
+ }
556
+ _initRunner() {
557
+ const runner = new Worker(getRunnerUrl("pglite"), {
558
+ name: "sigvelo-pglite",
559
+ type: "module"
560
+ });
561
+ runner.addEventListener("message", (event) => {
562
+ this._onMessage(event.data);
563
+ });
564
+ runner.addEventListener("error", (event) => {
565
+ this._pgliteLoading = false;
566
+ this._error = event.message || "PostgreSQL runner failed to load";
567
+ });
568
+ runner.postMessage({
569
+ type: "pglite-init",
570
+ dbName: this.dbName,
571
+ postgis: this.postgis
572
+ });
573
+ this._runner = runner;
574
+ }
575
+ _postToRunner(msg) {
576
+ this._runner?.postMessage(msg);
577
+ }
578
+ _onMessage(data) {
579
+ if (!isRecord(data)) return;
580
+ if (data.__sigvelo_pglite_ready === true) {
581
+ this._pgliteReady = true;
582
+ this._pgliteLoading = false;
583
+ if (this.setupSql) this._postToRunner({
584
+ type: "pglite-setup",
585
+ sql: this.setupSql
586
+ });
587
+ else this._requestSchema();
588
+ return;
589
+ }
590
+ if (data.__sigvelo_pglite_loading === true) {
591
+ this._loadingMessage = typeof data.message === "string" ? data.message : "Loading...";
592
+ this._pgliteLoading = true;
593
+ return;
594
+ }
595
+ if (data.__sigvelo_pglite_setup_done === true) {
596
+ this._pgliteLoading = false;
597
+ this._requestSchema();
598
+ return;
599
+ }
600
+ if (data.__sigvelo_pglite_result === true && Array.isArray(data.fields) && data.fields.every((field) => isRecord(field) && typeof field.name === "string" && typeof field.dataTypeID === "number") && Array.isArray(data.rows) && data.rows.every(isRecord) && typeof data.rowCount === "number" && typeof data.executionTimeMs === "number") {
601
+ this._resultFields = data.fields;
602
+ this._resultRows = data.rows;
603
+ this._rowCount = data.rowCount;
604
+ this._executionTimeMs = data.executionTimeMs;
605
+ this._error = null;
606
+ this._sortColumn = null;
607
+ this._sortDirection = null;
608
+ this._rightPane = "results";
609
+ if (this._compact) this._compactView = "results";
610
+ return;
611
+ }
612
+ if (data.__sigvelo_pglite_error === true && typeof data.id === "number" && typeof data.message === "string") {
613
+ if (data.id >= 0) {
614
+ this._error = data.message;
615
+ this._resultFields = [];
616
+ this._resultRows = [];
617
+ this._rightPane = "results";
618
+ if (this._compact) this._compactView = "results";
619
+ }
620
+ this._pgliteLoading = false;
621
+ return;
622
+ }
623
+ if (data.__sigvelo_pglite_schema === true && Array.isArray(data.tables) && data.tables.every((table) => isRecord(table) && typeof table.name === "string" && Array.isArray(table.columns) && table.columns.every((column) => isRecord(column) && typeof column.name === "string" && typeof column.type === "string" && typeof column.nullable === "boolean"))) {
624
+ this._schema = data.tables;
625
+ if (this._expandedTables.size === 0) this._expandedTables = new Set(this._schema.map((t) => t.name));
626
+ return;
627
+ }
628
+ if (data.__sigvelo_pglite_done === true) {
629
+ this._running = false;
630
+ this._pgliteLoading = false;
631
+ this._requestSchema();
632
+ return;
633
+ }
634
+ if (data.__sigvelo_pglite_reset_done === true) {
635
+ this._pgliteLoading = false;
636
+ this._resultFields = [];
637
+ this._resultRows = [];
638
+ this._error = null;
639
+ this._requestSchema();
640
+ return;
641
+ }
642
+ }
643
+ _requestSchema() {
644
+ this._postToRunner({ type: "pglite-schema" });
645
+ }
646
+ _runCode() {
647
+ if (!this._pgliteReady || !this._runner) return;
648
+ this._running = true;
649
+ this._error = null;
650
+ const id = ++this._execId;
651
+ this._postToRunner({
652
+ type: "pglite-exec",
653
+ sql: this._editedCode,
654
+ id
655
+ });
656
+ }
657
+ _onEditorChange(e) {
658
+ this._editedCode = e.target.value;
659
+ }
660
+ _resetDatabase() {
661
+ if (!this._runner) return;
662
+ this._postToRunner({
663
+ type: "pglite-reset",
664
+ setupSql: this.setupSql || void 0
665
+ });
666
+ }
667
+ _onKeydown(e) {
668
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
669
+ e.preventDefault();
670
+ this._runCode();
671
+ }
672
+ }
673
+ _toggleTable(name) {
674
+ const next = new Set(this._expandedTables);
675
+ if (next.has(name)) next.delete(name);
676
+ else next.add(name);
677
+ this._expandedTables = next;
678
+ }
679
+ _insertSelectQuery(tableName) {
680
+ this._editedCode = `SELECT * FROM "${tableName}" LIMIT 100;`;
681
+ if (this._compact) this._compactView = "code";
682
+ }
683
+ _toggleSort(columnName) {
684
+ if (this._sortColumn === columnName) {
685
+ this._sortDirection = this._sortDirection === "asc" ? "desc" : this._sortDirection === "desc" ? null : "asc";
686
+ if (this._sortDirection === null) this._sortColumn = null;
687
+ } else {
688
+ this._sortColumn = columnName;
689
+ this._sortDirection = "asc";
690
+ }
691
+ }
692
+ _getSortedRows() {
693
+ if (!this._sortColumn || !this._sortDirection) return this._resultRows;
694
+ const col = this._sortColumn;
695
+ const dir = this._sortDirection === "asc" ? 1 : -1;
696
+ return [...this._resultRows].sort((a, b) => {
697
+ const va = a[col];
698
+ const vb = b[col];
699
+ if (va == null && vb == null) return 0;
700
+ if (va == null) return dir;
701
+ if (vb == null) return -dir;
702
+ if (typeof va === "number" && typeof vb === "number") return (va - vb) * dir;
703
+ return formatCell(va).localeCompare(formatCell(vb)) * dir;
704
+ });
705
+ }
706
+ _renderEditorPane(slot) {
707
+ return html`
708
+ <div class="editor-pane" slot=${ifDefined(slot)}>
709
+ <div class="editor-pane__toolbar">
710
+ <span class="editor-pane__toolbar-title">SQL Editor</span>
711
+ <sigvelo-button
712
+ size="xs"
713
+ variant="ghost"
714
+ icon-label="Reset database"
715
+ ?disabled=${!this._pgliteReady || this._running}
716
+ @click=${() => this._resetDatabase()}
717
+ >
718
+ ${ICON_RESET}
719
+ </sigvelo-button>
720
+ <sigvelo-button
721
+ size="xs"
722
+ color="primary"
723
+ ?disabled=${!this._pgliteReady || this._running}
724
+ @click=${() => this._runCode()}
725
+ >
726
+ ${ICON_PLAY} Run
727
+ </sigvelo-button>
728
+ </div>
729
+ <div class="editor-pane__code">
730
+ <sigvelo-code-editor
731
+ .value=${this._editedCode}
732
+ language="sql"
733
+ editor-label=${this.editorLabel}
734
+ @sigvelo-change=${this._onEditorChange}
735
+ ></sigvelo-code-editor>
736
+ </div>
737
+ </div>
738
+ `;
739
+ }
740
+ _renderResultsView() {
741
+ if (this._error) return html`<div class="error-view">${this._error}</div>`;
742
+ if (this._resultFields.length === 0) return html`
743
+ <div class="results-empty">
744
+ No results yet. Type a SQL query and press Run or Ctrl+Enter.
745
+ </div>
746
+ `;
747
+ const rows = this._getSortedRows();
748
+ const displayRows = rows.slice(0, MAX_DISPLAY_ROWS);
749
+ const truncated = rows.length > MAX_DISPLAY_ROWS;
750
+ return html`
751
+ <div class="results-wrapper">
752
+ <div class="results-scroll">
753
+ <table class="results-table">
754
+ <thead>
755
+ <tr>
756
+ ${this._resultFields.map((f) => html`
757
+ <th
758
+ aria-sort=${this._sortColumn === f.name ? this._sortDirection === "desc" ? "descending" : "ascending" : "none"}
759
+ >
760
+ <button
761
+ class="results-table__sort"
762
+ type="button"
763
+ @click=${() => this._toggleSort(f.name)}
764
+ >
765
+ ${f.name}${this._sortColumn === f.name ? html`<span class="sort-indicator" aria-hidden="true"
766
+ >${this._sortDirection === "asc" ? "▲" : "▼"}</span
767
+ >` : nothing}
768
+ </button>
769
+ </th>
770
+ `)}
771
+ </tr>
772
+ </thead>
773
+ <tbody>
774
+ ${displayRows.map((row) => html`
775
+ <tr>
776
+ ${this._resultFields.map((f) => {
777
+ const val = row[f.name];
778
+ if (val === null || val === void 0) return html`<td class="cell--null">NULL</td>`;
779
+ return html`<td class=${typeof val === "number" ? "cell--number" : ""}>${formatCell(val)}</td>`;
780
+ })}
781
+ </tr>
782
+ `)}
783
+ </tbody>
784
+ </table>
785
+ </div>
786
+ <div class="results-status">
787
+ <span
788
+ >${truncated ? `Showing ${MAX_DISPLAY_ROWS} of ${rows.length}` : `${this._rowCount} row${this._rowCount !== 1 ? "s" : ""}`}</span
789
+ >
790
+ <span>${this._executionTimeMs}ms</span>
791
+ </div>
792
+ </div>
793
+ `;
794
+ }
795
+ _renderSchemaView() {
796
+ if (this._schema.length === 0) return html`<div class="schema-empty">No tables yet. Run a CREATE TABLE statement.</div>`;
797
+ return html`
798
+ <div class="schema-list">
799
+ ${this._schema.map((table) => {
800
+ const expanded = this._expandedTables.has(table.name);
801
+ return html`
802
+ <div class="schema-table">
803
+ <div class="schema-table__header">
804
+ <button
805
+ class="schema-table__disclosure"
806
+ type="button"
807
+ aria-expanded=${expanded}
808
+ @click=${() => this._toggleTable(table.name)}
809
+ >
810
+ <span
811
+ class="schema-table__chevron ${expanded ? "schema-table__chevron--open" : ""}"
812
+ aria-hidden="true"
813
+ >
814
+ ${ICON_CHEVRON}
815
+ </span>
816
+ <span class="schema-table__name">${table.name}</span>
817
+ </button>
818
+ <button
819
+ class="schema-table__action"
820
+ type="button"
821
+ aria-label="Insert SELECT query for ${table.name}"
822
+ @click=${() => this._insertSelectQuery(table.name)}
823
+ >
824
+ SELECT
825
+ </button>
826
+ </div>
827
+ ${expanded ? html`
828
+ <div class="schema-columns">
829
+ ${table.columns.map((col) => html`
830
+ <div class="schema-column">
831
+ <span class="schema-column__name">${col.name}</span>
832
+ <span class="schema-column__type">${col.type}</span>
833
+ </div>
834
+ `)}
835
+ </div>
836
+ ` : nothing}
837
+ </div>
838
+ `;
839
+ })}
840
+ </div>
841
+ `;
842
+ }
843
+ _renderRightPane(slot) {
844
+ const content = html`
845
+ <div class="right-pane__header">
846
+ <sigvelo-button-group label="Output toggle">
847
+ <sigvelo-button
848
+ size="xs"
849
+ variant=${this._rightPane === "results" ? "normal" : "ghost"}
850
+ color=${this._rightPane === "results" ? "primary" : "neutral"}
851
+ @click=${() => {
852
+ this._rightPane = "results";
853
+ }}
854
+ >
855
+ Results${this._resultRows.length > 0 ? html` (${Math.min(this._resultRows.length, MAX_DISPLAY_ROWS)})` : ""}
856
+ </sigvelo-button>
857
+ <sigvelo-button
858
+ size="xs"
859
+ variant=${this._rightPane === "schema" ? "normal" : "ghost"}
860
+ color=${this._rightPane === "schema" ? "primary" : "neutral"}
861
+ @click=${() => {
862
+ this._rightPane = "schema";
863
+ }}
864
+ >
865
+ Schema${this._schema.length > 0 ? html` (${this._schema.length})` : ""}
866
+ </sigvelo-button>
867
+ </sigvelo-button-group>
868
+ </div>
869
+ <div class="right-pane__content">
870
+ ${this._rightPane === "results" ? this._renderResultsView() : this._renderSchemaView()}
871
+ </div>
872
+ `;
873
+ return slot ? html`<div class="right-pane" slot=${slot}>${content}</div>` : html`<div class="right-pane">${content}</div>`;
874
+ }
875
+ _renderLoadingOverlay() {
876
+ if (!this._pgliteLoading) return nothing;
877
+ return html`
878
+ <div class="loading-overlay">
879
+ <sigvelo-spinner label="Loading PostgreSQL"></sigvelo-spinner>
880
+ <div class="loading-overlay__message">${this._loadingMessage}</div>
881
+ </div>
882
+ `;
883
+ }
884
+ _renderMobileNav() {
885
+ const btn = (view, label) => html`
886
+ <sigvelo-button
887
+ size="xs"
888
+ variant=${this._compactView === view ? "normal" : "ghost"}
889
+ color=${this._compactView === view ? "primary" : "neutral"}
890
+ @click=${() => {
891
+ this._compactView = view;
892
+ }}
893
+ >
894
+ ${label}
895
+ </sigvelo-button>
896
+ `;
897
+ return html`
898
+ <div class="mobile-nav">
899
+ <sigvelo-button-group label="View toggle">
900
+ ${btn("code", "Code")} ${btn("results", "Results")} ${btn("schema", "Schema")}
901
+ </sigvelo-button-group>
902
+ <sigvelo-button
903
+ size="xs"
904
+ variant="ghost"
905
+ icon-label="Reset database"
906
+ ?disabled=${!this._pgliteReady || this._running}
907
+ @click=${() => this._resetDatabase()}
908
+ >
909
+ ${ICON_RESET}
910
+ </sigvelo-button>
911
+ <sigvelo-button
912
+ size="xs"
913
+ color="primary"
914
+ icon-label="Run"
915
+ ?disabled=${!this._pgliteReady || this._running}
916
+ @click=${() => this._runCode()}
917
+ >
918
+ ${ICON_PLAY}
919
+ </sigvelo-button>
920
+ </div>
921
+ `;
922
+ }
923
+ render() {
924
+ if (this._compact) return html`
925
+ ${this._renderLoadingOverlay()} ${this._renderMobileNav()}
926
+ <div class="single-pane">
927
+ ${this._compactView === "code" ? this._renderEditorPane() : this._compactView === "results" ? this._renderResultsView() : this._renderSchemaView()}
928
+ </div>
929
+ `;
930
+ return html`
931
+ ${this._renderLoadingOverlay()}
932
+ <div class="content">
933
+ <sigvelo-splitter
934
+ orientation="vertical"
935
+ position="0.5"
936
+ style="--divider-min-position: 20%; --divider-max-position: 80%;"
937
+ >
938
+ ${this._renderEditorPane("start")} ${this._renderRightPane("end")}
939
+ </sigvelo-splitter>
940
+ </div>
941
+ `;
942
+ }
943
+ };
944
+ __decorate([property({ attribute: "setup-sql" })], SigveloSqlEditor.prototype, "setupSql", void 0);
945
+ __decorate([property()], SigveloSqlEditor.prototype, "code", void 0);
946
+ __decorate([property({ attribute: "db-name" })], SigveloSqlEditor.prototype, "dbName", void 0);
947
+ __decorate([property({ type: Boolean })], SigveloSqlEditor.prototype, "postgis", void 0);
948
+ __decorate([property({ attribute: "editor-label" })], SigveloSqlEditor.prototype, "editorLabel", void 0);
949
+ __decorate([state()], SigveloSqlEditor.prototype, "_editedCode", void 0);
950
+ __decorate([state()], SigveloSqlEditor.prototype, "_pgliteReady", void 0);
951
+ __decorate([state()], SigveloSqlEditor.prototype, "_pgliteLoading", void 0);
952
+ __decorate([state()], SigveloSqlEditor.prototype, "_loadingMessage", void 0);
953
+ __decorate([state()], SigveloSqlEditor.prototype, "_running", void 0);
954
+ __decorate([state()], SigveloSqlEditor.prototype, "_resultFields", void 0);
955
+ __decorate([state()], SigveloSqlEditor.prototype, "_resultRows", void 0);
956
+ __decorate([state()], SigveloSqlEditor.prototype, "_rowCount", void 0);
957
+ __decorate([state()], SigveloSqlEditor.prototype, "_executionTimeMs", void 0);
958
+ __decorate([state()], SigveloSqlEditor.prototype, "_error", void 0);
959
+ __decorate([state()], SigveloSqlEditor.prototype, "_schema", void 0);
960
+ __decorate([state()], SigveloSqlEditor.prototype, "_expandedTables", void 0);
961
+ __decorate([state()], SigveloSqlEditor.prototype, "_sortColumn", void 0);
962
+ __decorate([state()], SigveloSqlEditor.prototype, "_sortDirection", void 0);
963
+ __decorate([state()], SigveloSqlEditor.prototype, "_compact", void 0);
964
+ __decorate([state()], SigveloSqlEditor.prototype, "_compactView", void 0);
965
+ __decorate([state()], SigveloSqlEditor.prototype, "_rightPane", void 0);
966
+ customElements.define("sigvelo-sql-editor", SigveloSqlEditor);
967
+ //#endregion
968
+ export { SigveloSqlEditor as t };