db-player 0.0.0-stage → 0.5.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.
@@ -0,0 +1,4140 @@
1
+ import { A as workspaceQuery, B as regenerateMcpToken, C as databasesQuery, D as schemaQuery, E as rowsQuery, F as deleteFolder, G as testConnection, H as runQuery, I as deleteRows, J as updateRow, K as updateConnection, L as insertRow, M as createConnection, N as createFolder, O as sqlSchemaQuery, P as deleteConnection, R as moveConnection, S as connectionsQuery, T as mcpTokenQuery, U as setActiveTab, V as reorderTabs, W as setTabDatabase, X as createSsrRpc, Y as updateTabDraft, _ as Refresh, a as ChevronRight, b as Terminal, c as Database, d as EyeOff, f as FolderPlus, g as Plus, h as Play, i as ChevronLeft, j as closeTabs, k as tablesQuery, l as Dots, m as PanelLeft, n as ArrowDown, o as Close, p as Key, q as updateFolder, r as ArrowUp, s as Copy, t as EmptyState, u as Eye, v as Settings, w as foldersQuery, x as Trash, y as Table, z as openTab } from "./EmptyState-D1iyQsG1.js";
2
+ import { t as createServerFn } from "../server.js";
3
+ import { C as DB_KIND_INFO, D as folderInputSchema, E as connectionInputSchema, S as DB_KINDS, T as FOLDER_COLOR_NAMES, l as listConnections, s as getMcpToken, u as listFolders, w as FOLDER_COLORS, x as drivers } from "./store-Bb9KDsJx.js";
4
+ import { Activity, Suspense, createContext, use, useEffect, useImperativeHandle, useRef, useState } from "react";
5
+ import { CatchBoundary, HeadContent, Link, Outlet, Scripts, createFileRoute, createRootRouteWithContext, createRouter, getRouteApi, lazyRouteComponent, redirect, useNavigate, useParams, useRouter, useRouterState } from "@tanstack/react-router";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { z } from "zod";
8
+ import { QueryClient, keepPreviousData, useIsFetching, useMutation, useQuery, useQueryClient, useSuspenseQuery } from "@tanstack/react-query";
9
+ import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query";
10
+ import * as stylex from "@stylexjs/stylex";
11
+ import { useHotkey, useHotkeys } from "@tanstack/react-hotkeys";
12
+ import { Button } from "@base-ui/react/button";
13
+ import { Dialog } from "@base-ui/react/dialog";
14
+ import { useForm } from "@tanstack/react-form";
15
+ import { Checkbox } from "@base-ui/react/checkbox";
16
+ import { Input } from "@base-ui/react/input";
17
+ import { Select } from "@base-ui/react/select";
18
+ import { Radio } from "@base-ui/react/radio";
19
+ import { RadioGroup } from "@base-ui/react/radio-group";
20
+ import { ContextMenu } from "@base-ui/react/context-menu";
21
+ import { Menu } from "@base-ui/react/menu";
22
+ import { Tooltip } from "@base-ui/react/tooltip";
23
+ import { useDebouncedValue, useDebouncer } from "@tanstack/react-pacer";
24
+ import { columnResizingFeature, columnSizingFeature, createColumnHelper, tableFeatures, useTable } from "@tanstack/react-table";
25
+ import { useVirtualizer } from "@tanstack/react-virtual";
26
+ import { autocompletion, closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
27
+ import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
28
+ import { HighlightStyle, bracketMatching, indentOnInput, syntaxHighlighting } from "@codemirror/language";
29
+ import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
30
+ import { Compartment, EditorState, Prec } from "@codemirror/state";
31
+ import { EditorView, drawSelection, keymap, placeholder } from "@codemirror/view";
32
+ import { MySQL, SQLDialect, sql } from "@codemirror/lang-sql";
33
+ import { tags } from "@lezer/highlight";
34
+ import { Tabs } from "@base-ui/react/tabs";
35
+ import { createHash, timingSafeEqual } from "node:crypto";
36
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
37
+ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
38
+ // \0rolldown/runtime.js
39
+ var __defProp = Object.defineProperty;
40
+ var __exportAll = (all, no_symbols) => {
41
+ let target = {};
42
+ for (var name in all) __defProp(target, name, {
43
+ get: all[name],
44
+ enumerable: true
45
+ });
46
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
47
+ return target;
48
+ };
49
+ // src/tokens.stylex.ts
50
+ var color = {
51
+ bg: "var(--x1bd2iaf)",
52
+ surface: "var(--xgiwmlq)",
53
+ raised: "var(--x1sj141n)",
54
+ hover: "var(--x55kj9k)",
55
+ active: "var(--x1ko5s2s)",
56
+ border: "var(--x1ljawrx)",
57
+ borderStrong: "var(--x1wmecsq)",
58
+ logoTile: "var(--xxepf88)",
59
+ primary: "var(--x1b1m92g)",
60
+ primaryHover: "var(--x1djyeca)",
61
+ onPrimary: "var(--xz3jkde)",
62
+ text: "var(--xh1mtfv)",
63
+ muted: "var(--x1eegxt8)",
64
+ faint: "var(--x1y3jqnt)",
65
+ danger: "var(--x1rsrbe2)",
66
+ dangerBg: "var(--xzklvxa)",
67
+ success: "var(--x56xjt1)",
68
+ selection: "var(--x3itj7)",
69
+ synKeyword: "var(--x1oxlabo)",
70
+ synString: "var(--x54wwie)",
71
+ synNumber: "var(--x1j22127)",
72
+ synComment: "var(--x1jyogra)",
73
+ synFunction: "var(--x1ab7c27)",
74
+ synType: "var(--x1sy0dl7)",
75
+ tintRest: "var(--x189l5c3)",
76
+ tintHover: "var(--xcqutt3)",
77
+ tintActive: "var(--x2arnpg)",
78
+ backdrop: "var(--x1hemmdd)",
79
+ shadow: "var(--x1f48itt)",
80
+ __varGroupHash__: "x3qmmre"
81
+ };
82
+ var lightTheme = {
83
+ x3qmmre: "xl10l8c x3qmmre",
84
+ $$css: true
85
+ };
86
+ var darkTheme = {
87
+ x3qmmre: "xak9gdq x3qmmre",
88
+ $$css: true
89
+ };
90
+ var radius = {
91
+ sm: "var(--x1mekqhl)",
92
+ md: "var(--xt4ypks)",
93
+ lg: "var(--xgb9uqg)",
94
+ xl: "var(--xhrzkwl)",
95
+ full: "var(--x16stf2z)",
96
+ __varGroupHash__: "x1x9b9mp"
97
+ };
98
+ var fontSize = {
99
+ xs: "var(--x79uhsr)",
100
+ sm: "var(--xt6hdbs)",
101
+ md: "var(--x1bjvsbq)",
102
+ __varGroupHash__: "xe1lwr3"
103
+ };
104
+ var font = {
105
+ sans: "var(--x1q1zo9x)",
106
+ mono: "var(--x1p6nkrd)",
107
+ __varGroupHash__: "x1aipckl"
108
+ };
109
+ // src/components/PendingPane.tsx
110
+ function PendingPane() {
111
+ return /* @__PURE__ */ jsxs("div", {
112
+ role: "status",
113
+ "aria-label": "Loading",
114
+ className: "x98rzlu x78zum5 xdt5ytf x883omv x1tamke2",
115
+ children: [
116
+ /* @__PURE__ */ jsx("div", { className: "xn7wf0q x14fh20 x1tbzl01 x1sbju2s x4hg4is xa4qsjk x16grhtn xmix8c7" }),
117
+ /* @__PURE__ */ jsx("div", { className: "x1v9usgg xn7wf0q x14fh20 x1tbzl01 x1sbju2s x4hg4is xa4qsjk xh8yej3" }),
118
+ /* @__PURE__ */ jsx("div", { className: "x1v9usgg xn7wf0q x14fh20 x1tbzl01 x1sbju2s x4hg4is xa4qsjk xh8yej3" }),
119
+ /* @__PURE__ */ jsx("div", { className: "x1v9usgg xn7wf0q x14fh20 x1tbzl01 x1sbju2s x4hg4is xa4qsjk x65xoit" }),
120
+ /* @__PURE__ */ jsx("div", { className: "x1v9usgg xn7wf0q x14fh20 x1tbzl01 x1sbju2s x4hg4is xa4qsjk xinu3np" })
121
+ ]
122
+ });
123
+ }
124
+ // src/lib/workspace.ts
125
+ var EMPTY = {
126
+ tabs: [],
127
+ activeTabId: null,
128
+ revision: 0
129
+ };
130
+ function useWorkspace() {
131
+ return useQuery(workspaceQuery()).data ?? EMPTY;
132
+ }
133
+ /** The tab the URL points at. It is `null` on `/`, on a connection-only view, or for a stale id. */
134
+ function useActiveTab() {
135
+ const { tabId } = useParams({ strict: false });
136
+ return useWorkspace().tabs.find((t) => t.id === tabId) ?? null;
137
+ }
138
+ /** The Explorer follows the active tab and falls back to the connection in the URL. */
139
+ function useExplorerConnectionId() {
140
+ const { connectionId } = useParams({ strict: false });
141
+ return useActiveTab()?.target.connectionId ?? connectionId ?? null;
142
+ }
143
+ function useWorkspaceActions() {
144
+ const qc = useQueryClient();
145
+ const navigate = useNavigate();
146
+ const { tabId: urlTabId } = useParams({ strict: false });
147
+ const publish = (workspace) => qc.setQueryData(workspaceQuery().queryKey, workspace);
148
+ const edit = (change) => qc.setQueryData(workspaceQuery().queryKey, (w) => w ? change(w) : w);
149
+ /** An optimistic write that fails refetches the workspace, so the cache returns to the server's state. */
150
+ const settle = async (write) => {
151
+ try {
152
+ await write();
153
+ } catch {
154
+ await qc.invalidateQueries({ queryKey: workspaceQuery().queryKey });
155
+ }
156
+ };
157
+ const open = async (target, extra = {}) => {
158
+ const { workspace, tabId } = await openTab({ data: {
159
+ target,
160
+ ...extra
161
+ } });
162
+ publish(workspace);
163
+ await navigate({
164
+ to: "/t/$tabId",
165
+ params: { tabId }
166
+ });
167
+ };
168
+ return {
169
+ activate: (tabId) => navigate({
170
+ to: "/t/$tabId",
171
+ params: { tabId }
172
+ }),
173
+ openTable: (connectionId, database, table) => open({
174
+ kind: "table",
175
+ connectionId,
176
+ database,
177
+ table
178
+ }),
179
+ openSql: (connectionId) => open({
180
+ kind: "sql",
181
+ connectionId,
182
+ database: null
183
+ }),
184
+ duplicate: (tab) => open({
185
+ kind: "sql",
186
+ connectionId: tab.target.connectionId,
187
+ database: tab.target.database
188
+ }, {
189
+ sql: tab.sql,
190
+ after: tab.id
191
+ }),
192
+ close: async (ids, prefer) => {
193
+ const workspace = await closeTabs({ data: {
194
+ ids,
195
+ prefer
196
+ } });
197
+ publish(workspace);
198
+ if (urlTabId !== void 0 && !workspace.tabs.some((t) => t.id === urlTabId)) await navigate(workspace.activeTabId ? {
199
+ to: "/t/$tabId",
200
+ params: { tabId: workspace.activeTabId }
201
+ } : { to: "/" });
202
+ },
203
+ reorder: async (ids) => {
204
+ const order = new Map(ids.map((id, i) => [id, i]));
205
+ edit((w) => ({
206
+ ...w,
207
+ tabs: w.tabs.map((t) => ({
208
+ ...t,
209
+ position: order.get(t.id) ?? t.position
210
+ })).toSorted((a, b) => a.position - b.position)
211
+ }));
212
+ await settle(async () => publish(await reorderTabs({ data: { ids } })));
213
+ },
214
+ setDatabase: async (id, database) => {
215
+ edit((w) => ({
216
+ ...w,
217
+ tabs: w.tabs.map((t) => t.id === id && t.target.kind === "sql" ? {
218
+ ...t,
219
+ target: {
220
+ ...t.target,
221
+ database
222
+ }
223
+ } : t)
224
+ }));
225
+ await settle(() => setTabDatabase({ data: {
226
+ id,
227
+ database
228
+ } }));
229
+ },
230
+ saveDraft: async (id, sql) => {
231
+ edit((w) => ({
232
+ ...w,
233
+ tabs: w.tabs.map((t) => t.id === id ? {
234
+ ...t,
235
+ sql
236
+ } : t)
237
+ }));
238
+ await settle(() => updateTabDraft({ data: {
239
+ id,
240
+ sql
241
+ } }));
242
+ },
243
+ markActive: async (id) => {
244
+ edit((w) => ({
245
+ ...w,
246
+ activeTabId: id
247
+ }));
248
+ await settle(() => setActiveTab({ data: { id } }));
249
+ }
250
+ };
251
+ }
252
+ var DEFAULT_LAYOUT = {
253
+ sidebarOpen: true,
254
+ sidebarWidth: 264,
255
+ explorerWidth: 248
256
+ };
257
+ var COOKIE = "dbp-layout";
258
+ var getLayout = createServerFn({ method: "GET" }).handler(createSsrRpc("767e3c79a7a604bea062fbfee2e3cdeed9ee8c108dce8db65bf989a96985f89a"));
259
+ function writeCookie(layout) {
260
+ document.cookie = `${COOKIE}=${encodeURIComponent(JSON.stringify(layout))}; path=/; max-age=31536000; samesite=lax`;
261
+ }
262
+ var LayoutContext = createContext(null);
263
+ function LayoutProvider({ initial, children }) {
264
+ const [layout, setLayout] = useState(initial);
265
+ const update = (patch, persist = true) => {
266
+ const next = {
267
+ ...layout,
268
+ ...patch
269
+ };
270
+ setLayout(next);
271
+ if (persist) writeCookie(next);
272
+ };
273
+ return /* @__PURE__ */ jsx(LayoutContext, {
274
+ value: {
275
+ layout,
276
+ update
277
+ },
278
+ children
279
+ });
280
+ }
281
+ function useLayout() {
282
+ const ctx = use(LayoutContext);
283
+ if (!ctx) throw new Error("useLayout needs a LayoutProvider");
284
+ return ctx;
285
+ }
286
+ // src/lib/mutations.ts
287
+ function useSidebarMutations() {
288
+ const qc = useQueryClient();
289
+ const refresh = () => Promise.all([qc.invalidateQueries({ queryKey: ["folders"] }), qc.invalidateQueries({ queryKey: ["connections"] })]);
290
+ return {
291
+ createFolder: useMutation({
292
+ mutationFn: createFolder,
293
+ onSuccess: refresh
294
+ }),
295
+ updateFolder: useMutation({
296
+ mutationFn: updateFolder,
297
+ onSuccess: refresh
298
+ }),
299
+ deleteFolder: useMutation({
300
+ mutationFn: deleteFolder,
301
+ onSuccess: refresh
302
+ }),
303
+ createConnection: useMutation({
304
+ mutationFn: createConnection,
305
+ onSuccess: refresh
306
+ }),
307
+ updateConnection: useMutation({
308
+ mutationFn: updateConnection,
309
+ onSuccess: async (_d, vars) => {
310
+ await refresh();
311
+ await qc.invalidateQueries({ queryKey: ["databases", vars.data.id] });
312
+ await qc.invalidateQueries({ queryKey: ["tables", vars.data.id] });
313
+ await qc.invalidateQueries({ queryKey: ["sqlSchema", vars.data.id] });
314
+ await qc.invalidateQueries({ queryKey: ["schema", vars.data.id] });
315
+ await qc.invalidateQueries({ queryKey: ["rows", vars.data.id] });
316
+ }
317
+ }),
318
+ moveConnection: useMutation({
319
+ mutationFn: moveConnection,
320
+ onSuccess: refresh
321
+ }),
322
+ deleteConnection: useMutation({
323
+ mutationFn: deleteConnection,
324
+ onSuccess: async (_workspace, vars) => {
325
+ qc.removeQueries({ queryKey: ["databases", vars.data.id] });
326
+ await Promise.all([refresh(), qc.invalidateQueries({ queryKey: workspaceQuery().queryKey })]);
327
+ }
328
+ }),
329
+ testConnection: useMutation({ mutationFn: testConnection })
330
+ };
331
+ }
332
+ // src/lib/format.ts
333
+ function formatCell(value) {
334
+ if (value === null || value === void 0) return "NULL";
335
+ if (typeof value === "object") return JSON.stringify(value);
336
+ return String(value);
337
+ }
338
+ function errorMessage(error) {
339
+ return error instanceof Error ? error.message : String(error);
340
+ }
341
+ // src/components/ui/Button.tsx
342
+ var styles$8 = {
343
+ base: {
344
+ k1xSpc: "x3nfvp2",
345
+ kGNEyG: "x6s0dn4",
346
+ kjj79g: "xl56j7k",
347
+ kOIVth: "x17d4w8g",
348
+ kZKoxP: "x1jgo0hl",
349
+ kg3NbH: "x1awh872",
350
+ kMv6JI: "x134rb1s",
351
+ kGuDYH: "xqtwvag",
352
+ k63SB2: "xk50ysn",
353
+ kaIpWk: "xvjf9rd",
354
+ kMzoRj: "xmkeg23",
355
+ ksu8eU: "x1y0btm7",
356
+ kkrTdU: "x1ypdohk xtgyt42",
357
+ khDVqt: "xuxw1ft",
358
+ kfSwDN: "x87ps6o",
359
+ k1ekBW: "xv65o4f",
360
+ kIyJzY: "x1qfwjxi",
361
+ kAMwcw: "x6setmy",
362
+ k3aq6I: "x1c071of x15qbw3q",
363
+ kI3sdo: "x1a2a7pz xn0s4wz",
364
+ kInvED: "x1y3gkto",
365
+ kSiTet: "x1hc1fzr x1t5pwa4",
366
+ $$css: true
367
+ },
368
+ primary: {
369
+ kWkggS: "xq83s4c xds5qq0",
370
+ kVAM5u: "xxuvl4d",
371
+ kMwMTN: "x18x1ra",
372
+ $$css: true
373
+ },
374
+ secondary: {
375
+ kWkggS: "x14fh20 x17ec2s3",
376
+ kVAM5u: "x4s12hp",
377
+ kMwMTN: "x122w267",
378
+ $$css: true
379
+ },
380
+ ghost: {
381
+ kWkggS: "xjbqb8w x1ncw40y xgb2fgl",
382
+ kVAM5u: "x9r1u3d",
383
+ kMwMTN: "x122w267",
384
+ $$css: true
385
+ },
386
+ danger: {
387
+ kWkggS: "x185or1n x1am0jh4",
388
+ kVAM5u: "xb1jnos",
389
+ kMwMTN: "xb0vdwp",
390
+ $$css: true
391
+ },
392
+ icon: {
393
+ kzqmXN: "x1a5d0zf",
394
+ kg3NbH: "xnjsko4",
395
+ $$css: true
396
+ },
397
+ small: {
398
+ kZKoxP: "x11cxg1j",
399
+ kg3NbH: "xdqyycr",
400
+ $$css: true
401
+ },
402
+ smallIcon: {
403
+ kzqmXN: "x5tfkcl",
404
+ kg3NbH: "xnjsko4",
405
+ $$css: true
406
+ },
407
+ fill: {
408
+ kzqmXN: "xh8yej3",
409
+ $$css: true
410
+ }
411
+ };
412
+ function Button$1({ variant = "secondary", icon, small, fill, ...rest }) {
413
+ return /* @__PURE__ */ jsx(Button, {
414
+ ...rest,
415
+ ...stylex.props(styles$8.base, styles$8[variant], small && styles$8.small, icon && (small ? styles$8.smallIcon : styles$8.icon), fill && styles$8.fill)
416
+ });
417
+ }
418
+ // src/components/ui/Dialog.tsx
419
+ function Dialog$1({ open, onOpenChange, title, wide, children }) {
420
+ const [closing, setClosing] = useState(false);
421
+ return /* @__PURE__ */ jsx(Dialog.Root, {
422
+ open: open && !closing,
423
+ onOpenChange: (next) => next ? onOpenChange(true) : setClosing(true),
424
+ onOpenChangeComplete: (isOpen) => {
425
+ if (!isOpen && closing) onOpenChange(false);
426
+ },
427
+ children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [/* @__PURE__ */ jsx(Dialog.Backdrop, { className: "xixxii4 x10a8y8t xa9119d x1spjluv x1hc1fzr x1hw7zpd xiyypsa x19991ni xv3hhg7 x1ya1k7h x6setmy" }), /* @__PURE__ */ jsxs(Dialog.Popup, {
428
+ ...{
429
+ 0: { className: "xixxii4 x10a8y8t x1bpp3o7 xg7h5cd xfa8aoj xsjcku0 x1xoggjn x1odjw0f x1qhigcl x14fh20 xbg4agz xmkeg23 x1y0btm7 x4s12hp x125ndz9 x1a2a7pz x1g0ag68 x1hc1fzr x1hw7zpd xiyypsa xvvkyix xy54nku x1hoy9cc x6o7n8i xv3hhg7 x1ya1k7h x6setmy" },
430
+ 1: { className: "xixxii4 x10a8y8t x1bpp3o7 xg7h5cd xfa8aoj x1xoggjn x1odjw0f x1qhigcl x14fh20 xbg4agz xmkeg23 x1y0btm7 x4s12hp x125ndz9 x1a2a7pz x1g0ag68 x1hc1fzr x1hw7zpd xiyypsa xvvkyix xy54nku x1hoy9cc x6o7n8i xv3hhg7 x1ya1k7h x6setmy xpn72k7" }
431
+ }[!!wide << 0],
432
+ children: [/* @__PURE__ */ jsx(Dialog.Title, {
433
+ className: "x1ghz6dp x1yztbdb x19u1y1h x1s688f",
434
+ children: title
435
+ }), children]
436
+ })] })
437
+ });
438
+ }
439
+ function DialogFooter({ children }) {
440
+ return /* @__PURE__ */ jsx("div", {
441
+ className: "x78zum5 x13a6bvl x167g77z x1sy10c2",
442
+ children
443
+ });
444
+ }
445
+ // src/components/ConfirmDialog.tsx
446
+ function ConfirmDialog({ title, message, confirmLabel, busy, onConfirm, onCancel }) {
447
+ const [error, setError] = useState(null);
448
+ const confirm = async () => {
449
+ setError(null);
450
+ try {
451
+ await onConfirm();
452
+ } catch (e) {
453
+ setError(errorMessage(e));
454
+ }
455
+ };
456
+ return /* @__PURE__ */ jsxs(Dialog$1, {
457
+ open: true,
458
+ onOpenChange: (open) => !open && onCancel(),
459
+ title,
460
+ children: [
461
+ /* @__PURE__ */ jsx("p", {
462
+ className: "x1ghz6dp xfifm61 x1evy7pa",
463
+ children: message
464
+ }),
465
+ error ? /* @__PURE__ */ jsx("div", {
466
+ role: "alert",
467
+ className: "x1jp4ehj xfifm61 xb0vdwp x13faqbe",
468
+ children: error
469
+ }) : null,
470
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(Button$1, {
471
+ onClick: onCancel,
472
+ children: "Cancel"
473
+ }), /* @__PURE__ */ jsx(Button$1, {
474
+ variant: "danger",
475
+ disabled: busy,
476
+ onClick: () => void confirm(),
477
+ children: confirmLabel
478
+ })] })
479
+ ]
480
+ });
481
+ }
482
+ // src/components/ui/Checkbox.tsx
483
+ function Checkbox$1({ checked, onCheckedChange, label, ariaLabel }) {
484
+ return /* @__PURE__ */ jsxs("label", {
485
+ className: "x3nfvp2 x6s0dn4 x167g77z xfifm61 x1ypdohk",
486
+ children: [/* @__PURE__ */ jsx(Checkbox.Root, {
487
+ checked,
488
+ onCheckedChange,
489
+ "aria-label": ariaLabel ?? label,
490
+ className: "x3nfvp2 x6s0dn4 xl56j7k x1kky2od xlup9mm x1717udv xgwgzc4 xn7wf0q xmkeg23 x1y0btm7 xxuvl4d x7d8hld x1ypdohk",
491
+ children: /* @__PURE__ */ jsx(Checkbox.Indicator, { children: /* @__PURE__ */ jsx("svg", {
492
+ width: "10",
493
+ height: "10",
494
+ viewBox: "0 0 10 10",
495
+ fill: "none",
496
+ stroke: "currentColor",
497
+ strokeWidth: "1.6",
498
+ children: /* @__PURE__ */ jsx("path", { d: "M2 5.2 4.2 7.4 8 2.8" })
499
+ }) })
500
+ }), label]
501
+ });
502
+ }
503
+ // src/components/ui/Field.tsx
504
+ function Field({ label, error, children }) {
505
+ return /* @__PURE__ */ jsxs("label", {
506
+ className: "x78zum5 xdt5ytf x1jnr06f xeuugli",
507
+ children: [
508
+ /* @__PURE__ */ jsx("span", {
509
+ className: "x1j6dyjg x1tviuuy",
510
+ children: label
511
+ }),
512
+ children,
513
+ error ? /* @__PURE__ */ jsx("span", {
514
+ className: "x1j6dyjg xb0vdwp",
515
+ children: error
516
+ }) : null
517
+ ]
518
+ });
519
+ }
520
+ // src/components/ui/Input.tsx
521
+ function Input$1({ mono, inputRef, ...rest }) {
522
+ return /* @__PURE__ */ jsx(Input, {
523
+ ref: inputRef,
524
+ ...rest,
525
+ ...{
526
+ 0: { className: "xh8yej3 x1jgo0hl xdqyycr x134rb1s xqtwvag xgwgzc4 x122w267 xvjf9rd xmkeg23 x1y0btm7 x4s12hp x4mu1xn x1a2a7pz xq90yva x1qfwjxi x6setmy x1g6wmp5" },
527
+ 1: { className: "xh8yej3 x1jgo0hl xdqyycr xqtwvag xgwgzc4 x122w267 xvjf9rd xmkeg23 x1y0btm7 x4s12hp x4mu1xn x1a2a7pz xq90yva x1qfwjxi x6setmy x1g6wmp5 xxb57xf xss6m8b" }
528
+ }[!!mono << 0]
529
+ });
530
+ }
531
+ // src/components/ui/popupStyles.ts
532
+ var popup = {
533
+ positioner: {
534
+ kY2c9j: "x1aciyhk",
535
+ $$css: true
536
+ },
537
+ surface: {
538
+ kmVPX3: "xfawy5m",
539
+ kWkggS: "x14fh20",
540
+ kaIpWk: "xr11lhz",
541
+ kMzoRj: "xmkeg23",
542
+ ksu8eU: "x1y0btm7",
543
+ kVAM5u: "x4s12hp",
544
+ kGVxlE: "x165p7zc",
545
+ kI3sdo: "x1a2a7pz",
546
+ k3nNDw: "xctpfpq",
547
+ kSiTet: "x1hc1fzr x1hw7zpd xiyypsa",
548
+ k3aq6I: "x3oybdh x1qdyiyv x14up28i",
549
+ k1ekBW: "x6o7n8i",
550
+ kIyJzY: "x19jr8i9 x1ya1k7h",
551
+ kAMwcw: "x6setmy",
552
+ $$css: true
553
+ }
554
+ };
555
+ // src/components/ui/Select.tsx
556
+ var styles$7 = { popup: {
557
+ k7Eaqz: "x80t9vs",
558
+ kskxy: "xq3t0pi",
559
+ kORKVm: "x1odjw0f",
560
+ $$css: true
561
+ } };
562
+ function Select$1({ value, onValueChange, options, ariaLabel }) {
563
+ return /* @__PURE__ */ jsxs(Select.Root, {
564
+ value,
565
+ onValueChange: (v) => {
566
+ if (v !== null) onValueChange(v);
567
+ },
568
+ items: options,
569
+ children: [/* @__PURE__ */ jsxs(Select.Trigger, {
570
+ "aria-label": ariaLabel,
571
+ className: "x78zum5 x6s0dn4 x1qughib x167g77z xh8yej3 x1jgo0hl xdqyycr x134rb1s xqtwvag xgwgzc4 x122w267 xvjf9rd xmkeg23 x1y0btm7 x4s12hp x1ckbk5l x1a2a7pz x1ypdohk xshfolx x1qfwjxi x6setmy",
572
+ children: [/* @__PURE__ */ jsx(Select.Value, {}), /* @__PURE__ */ jsx(Select.Icon, { children: /* @__PURE__ */ jsx("svg", {
573
+ width: "10",
574
+ height: "10",
575
+ viewBox: "0 0 10 10",
576
+ fill: "none",
577
+ stroke: "currentColor",
578
+ strokeWidth: "1.4",
579
+ children: /* @__PURE__ */ jsx("path", { d: "m2 3.5 3 3 3-3" })
580
+ }) })]
581
+ }), /* @__PURE__ */ jsx(Select.Portal, { children: /* @__PURE__ */ jsx(Select.Positioner, {
582
+ sideOffset: 4,
583
+ ...stylex.props(popup.positioner),
584
+ children: /* @__PURE__ */ jsx(Select.Popup, {
585
+ ...stylex.props(popup.surface, styles$7.popup),
586
+ children: options.map((o) => /* @__PURE__ */ jsx(Select.Item, {
587
+ value: o.value,
588
+ className: "x78zum5 x6s0dn4 x1jgo0hl xdqyycr xqtwvag xn7wf0q xt0e3qv x1a2a7pz xjbqb8w x1gjw99y",
589
+ children: /* @__PURE__ */ jsx(Select.ItemText, { children: o.label })
590
+ }, o.value))
591
+ })
592
+ }) })]
593
+ });
594
+ }
595
+ // src/components/ConnectionDialog.tsx
596
+ var UNGROUPED = "__ungrouped__";
597
+ function ConnectionDialog({ connection, folderId, folders, onClose }) {
598
+ const m = useSidebarMutations();
599
+ const [test, setTest] = useState(null);
600
+ const [submitError, setSubmitError] = useState(null);
601
+ const defaults = connection ? {
602
+ ...connection,
603
+ password: ""
604
+ } : {
605
+ folderId: folderId ?? null,
606
+ name: "",
607
+ kind: "mysql",
608
+ host: "127.0.0.1",
609
+ port: DB_KIND_INFO.mysql.defaultPort,
610
+ user: "",
611
+ password: "",
612
+ database: null,
613
+ secure: false
614
+ };
615
+ const form = useForm({
616
+ defaultValues: defaults,
617
+ validators: { onChange: connectionInputSchema },
618
+ onSubmit: async ({ value }) => {
619
+ setSubmitError(null);
620
+ try {
621
+ if (connection) await m.updateConnection.mutateAsync({ data: {
622
+ id: connection.id,
623
+ input: value
624
+ } });
625
+ else await m.createConnection.mutateAsync({ data: value });
626
+ onClose();
627
+ } catch (error) {
628
+ setSubmitError(errorMessage(error));
629
+ }
630
+ }
631
+ });
632
+ const runTest = async () => {
633
+ setTest({
634
+ ok: true,
635
+ message: "Testing..."
636
+ });
637
+ try {
638
+ const { version } = await m.testConnection.mutateAsync({ data: {
639
+ id: connection?.id ?? null,
640
+ input: form.state.values
641
+ } });
642
+ setTest({
643
+ ok: true,
644
+ message: `Connected. Server version ${version}`
645
+ });
646
+ } catch (error) {
647
+ setTest({
648
+ ok: false,
649
+ message: errorMessage(error)
650
+ });
651
+ }
652
+ };
653
+ const err = (meta) => meta.isTouched ? meta.errors[0]?.message : void 0;
654
+ return /* @__PURE__ */ jsx(Dialog$1, {
655
+ open: true,
656
+ onOpenChange: (open) => !open && onClose(),
657
+ title: connection ? "Edit connection" : "New connection",
658
+ children: /* @__PURE__ */ jsxs("form", {
659
+ onSubmit: (e) => {
660
+ e.preventDefault();
661
+ form.handleSubmit();
662
+ },
663
+ className: "x78zum5 xdt5ytf x1v2ro7d",
664
+ children: [
665
+ /* @__PURE__ */ jsx(form.Field, {
666
+ name: "name",
667
+ children: (f) => /* @__PURE__ */ jsx(Field, {
668
+ label: "Name",
669
+ error: err(f.state.meta),
670
+ children: /* @__PURE__ */ jsx(Input$1, {
671
+ autoFocus: true,
672
+ name: "name",
673
+ value: f.state.value,
674
+ onBlur: f.handleBlur,
675
+ onChange: (e) => f.handleChange(e.target.value)
676
+ })
677
+ })
678
+ }),
679
+ /* @__PURE__ */ jsxs("div", {
680
+ className: "xrvj5dj x1o61qjw x1v2ro7d",
681
+ children: [/* @__PURE__ */ jsx(form.Field, {
682
+ name: "kind",
683
+ children: (f) => /* @__PURE__ */ jsx(Field, {
684
+ label: "Type",
685
+ children: /* @__PURE__ */ jsx(Select$1, {
686
+ ariaLabel: "Type",
687
+ value: f.state.value,
688
+ options: DB_KINDS.map((k) => ({
689
+ value: k,
690
+ label: DB_KIND_INFO[k].label
691
+ })),
692
+ onValueChange: (kind) => {
693
+ const previousDefault = DB_KIND_INFO[f.state.value].defaultPort;
694
+ f.handleChange(kind);
695
+ if (form.getFieldValue("port") === previousDefault) form.setFieldValue("port", DB_KIND_INFO[kind].defaultPort);
696
+ }
697
+ })
698
+ })
699
+ }), /* @__PURE__ */ jsx(form.Field, {
700
+ name: "folderId",
701
+ children: (f) => /* @__PURE__ */ jsx(Field, {
702
+ label: "Folder",
703
+ children: /* @__PURE__ */ jsx(Select$1, {
704
+ ariaLabel: "Folder",
705
+ value: f.state.value ?? UNGROUPED,
706
+ options: [{
707
+ value: UNGROUPED,
708
+ label: "Ungrouped"
709
+ }, ...folders.map((x) => ({
710
+ value: x.id,
711
+ label: x.name
712
+ }))],
713
+ onValueChange: (v) => f.handleChange(v === UNGROUPED ? null : v)
714
+ })
715
+ })
716
+ })]
717
+ }),
718
+ /* @__PURE__ */ jsxs("div", {
719
+ className: "xrvj5dj x9flyx0 x1v2ro7d",
720
+ children: [/* @__PURE__ */ jsx(form.Field, {
721
+ name: "host",
722
+ children: (f) => /* @__PURE__ */ jsx(Field, {
723
+ label: "Host",
724
+ error: err(f.state.meta),
725
+ children: /* @__PURE__ */ jsx(Input$1, {
726
+ name: "host",
727
+ value: f.state.value,
728
+ onBlur: f.handleBlur,
729
+ onChange: (e) => f.handleChange(e.target.value)
730
+ })
731
+ })
732
+ }), /* @__PURE__ */ jsx(form.Field, {
733
+ name: "port",
734
+ children: (f) => /* @__PURE__ */ jsx(Field, {
735
+ label: "Port",
736
+ error: err(f.state.meta),
737
+ children: /* @__PURE__ */ jsx(Input$1, {
738
+ name: "port",
739
+ mono: true,
740
+ inputMode: "numeric",
741
+ value: String(f.state.value),
742
+ onBlur: f.handleBlur,
743
+ onChange: (e) => f.handleChange(Number(e.target.value.replace(/\D/g, "")))
744
+ })
745
+ })
746
+ })]
747
+ }),
748
+ /* @__PURE__ */ jsxs("div", {
749
+ className: "xrvj5dj x1o61qjw x1v2ro7d",
750
+ children: [/* @__PURE__ */ jsx(form.Field, {
751
+ name: "user",
752
+ children: (f) => /* @__PURE__ */ jsx(Field, {
753
+ label: "User",
754
+ error: err(f.state.meta),
755
+ children: /* @__PURE__ */ jsx(Input$1, {
756
+ name: "user",
757
+ value: f.state.value,
758
+ onBlur: f.handleBlur,
759
+ onChange: (e) => f.handleChange(e.target.value)
760
+ })
761
+ })
762
+ }), /* @__PURE__ */ jsx(form.Field, {
763
+ name: "password",
764
+ children: (f) => /* @__PURE__ */ jsx(Field, {
765
+ label: "Password",
766
+ children: /* @__PURE__ */ jsx(Input$1, {
767
+ name: "password",
768
+ type: "password",
769
+ autoComplete: "new-password",
770
+ placeholder: connection?.hasPassword ? "Unchanged" : "",
771
+ value: f.state.value,
772
+ onBlur: f.handleBlur,
773
+ onChange: (e) => f.handleChange(e.target.value)
774
+ })
775
+ })
776
+ })]
777
+ }),
778
+ /* @__PURE__ */ jsx(form.Field, {
779
+ name: "database",
780
+ children: (f) => /* @__PURE__ */ jsx(Field, {
781
+ label: "Default database (optional)",
782
+ children: /* @__PURE__ */ jsx(Input$1, {
783
+ name: "database",
784
+ value: f.state.value ?? "",
785
+ onBlur: f.handleBlur,
786
+ onChange: (e) => f.handleChange(e.target.value || null)
787
+ })
788
+ })
789
+ }),
790
+ /* @__PURE__ */ jsx(form.Field, {
791
+ name: "secure",
792
+ children: (f) => /* @__PURE__ */ jsx(Checkbox$1, {
793
+ label: "Use TLS",
794
+ checked: f.state.value,
795
+ onCheckedChange: f.handleChange
796
+ })
797
+ }),
798
+ test ? /* @__PURE__ */ jsx("div", {
799
+ role: "status",
800
+ ...{
801
+ 0: { className: "xfifm61 xxb57xf x13faqbe xb0vdwp" },
802
+ 1: { className: "xfifm61 xxb57xf x13faqbe x1n8ird4" }
803
+ }[!!test.ok << 0],
804
+ children: test.message
805
+ }) : null,
806
+ submitError ? /* @__PURE__ */ jsx("div", {
807
+ className: "xfifm61 xxb57xf x13faqbe xb0vdwp",
808
+ children: submitError
809
+ }) : null,
810
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
811
+ /* @__PURE__ */ jsx(Button$1, {
812
+ type: "button",
813
+ onClick: runTest,
814
+ disabled: m.testConnection.isPending,
815
+ children: "Test connection"
816
+ }),
817
+ /* @__PURE__ */ jsx("div", { className: "x98rzlu" }),
818
+ /* @__PURE__ */ jsx(Button$1, {
819
+ type: "button",
820
+ onClick: onClose,
821
+ children: "Cancel"
822
+ }),
823
+ /* @__PURE__ */ jsx(form.Subscribe, {
824
+ selector: (s) => s.canSubmit && !s.isSubmitting,
825
+ children: (ok) => /* @__PURE__ */ jsx(Button$1, {
826
+ type: "submit",
827
+ variant: "primary",
828
+ disabled: !ok,
829
+ children: connection ? "Save" : "Create connection"
830
+ })
831
+ })
832
+ ] })
833
+ ]
834
+ })
835
+ });
836
+ }
837
+ // src/components/ui/ColorSwatchPicker.tsx
838
+ var _temp$4 = {
839
+ kzqmXN: "x17z2i9w",
840
+ kZKoxP: "x17rw0jw",
841
+ kmVPX3: "x1717udv",
842
+ kaIpWk: "x16rqkct",
843
+ kMzoRj: "xdh2fpr",
844
+ ksu8eU: "x1y0btm7",
845
+ kVAM5u: "x9r1u3d",
846
+ kkrTdU: "x1ypdohk",
847
+ kI3sdo: "x1a2a7pz",
848
+ "$$css": true
849
+ };
850
+ var styles$6 = {
851
+ swatch: (bg) => [
852
+ _temp$4,
853
+ {
854
+ kWkggS: bg != null ? "xl8spv7" : bg,
855
+ $$css: true
856
+ },
857
+ { "--x-backgroundColor": bg != null ? bg : void 0 }
858
+ ],
859
+ checked: {
860
+ kVAM5u: "x1b7y1cz",
861
+ $$css: true
862
+ }
863
+ };
864
+ function ColorSwatchPicker({ value, onValueChange }) {
865
+ return /* @__PURE__ */ jsx(RadioGroup, {
866
+ value,
867
+ onValueChange: (v) => onValueChange(v),
868
+ "aria-label": "Folder color",
869
+ className: "x78zum5 x167g77z x1a02dak",
870
+ children: FOLDER_COLOR_NAMES.map((name) => /* @__PURE__ */ jsx(Radio.Root, {
871
+ value: name,
872
+ "aria-label": name,
873
+ title: name,
874
+ ...stylex.props(styles$6.swatch(FOLDER_COLORS[name]), value === name && styles$6.checked)
875
+ }, name))
876
+ });
877
+ }
878
+ // src/components/FolderDialog.tsx
879
+ function FolderDialog({ folder, onClose }) {
880
+ const m = useSidebarMutations();
881
+ const [submitError, setSubmitError] = useState(null);
882
+ const form = useForm({
883
+ defaultValues: {
884
+ name: folder?.name ?? "",
885
+ color: folder?.color ?? "blue"
886
+ },
887
+ validators: { onChange: folderInputSchema },
888
+ onSubmit: async ({ value }) => {
889
+ setSubmitError(null);
890
+ try {
891
+ if (folder) await m.updateFolder.mutateAsync({ data: {
892
+ id: folder.id,
893
+ input: value
894
+ } });
895
+ else await m.createFolder.mutateAsync({ data: value });
896
+ onClose();
897
+ } catch (error) {
898
+ setSubmitError(errorMessage(error));
899
+ }
900
+ }
901
+ });
902
+ return /* @__PURE__ */ jsx(Dialog$1, {
903
+ open: true,
904
+ onOpenChange: (open) => !open && onClose(),
905
+ title: folder ? "Edit folder" : "New folder",
906
+ children: /* @__PURE__ */ jsxs("form", {
907
+ onSubmit: (e) => {
908
+ e.preventDefault();
909
+ form.handleSubmit();
910
+ },
911
+ className: "x78zum5 xdt5ytf x1af02g3",
912
+ children: [
913
+ /* @__PURE__ */ jsx(form.Field, {
914
+ name: "name",
915
+ children: (field) => /* @__PURE__ */ jsx(Field, {
916
+ label: "Name",
917
+ error: field.state.meta.isTouched ? field.state.meta.errors[0]?.message : void 0,
918
+ children: /* @__PURE__ */ jsx(Input$1, {
919
+ autoFocus: true,
920
+ name: "name",
921
+ value: field.state.value,
922
+ onBlur: field.handleBlur,
923
+ onChange: (e) => field.handleChange(e.target.value)
924
+ })
925
+ })
926
+ }),
927
+ /* @__PURE__ */ jsx(form.Field, {
928
+ name: "color",
929
+ children: (field) => /* @__PURE__ */ jsx(Field, {
930
+ label: "Color",
931
+ children: /* @__PURE__ */ jsx(ColorSwatchPicker, {
932
+ value: field.state.value,
933
+ onValueChange: field.handleChange
934
+ })
935
+ })
936
+ }),
937
+ submitError ? /* @__PURE__ */ jsx("div", {
938
+ role: "alert",
939
+ className: "xfifm61 xb0vdwp x13faqbe",
940
+ children: submitError
941
+ }) : null,
942
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(Button$1, {
943
+ type: "button",
944
+ onClick: onClose,
945
+ children: "Cancel"
946
+ }), /* @__PURE__ */ jsx(form.Subscribe, {
947
+ selector: (s) => s.canSubmit && !s.isSubmitting,
948
+ children: (ok) => /* @__PURE__ */ jsx(Button$1, {
949
+ type: "submit",
950
+ variant: "primary",
951
+ disabled: !ok,
952
+ children: folder ? "Save" : "Create folder"
953
+ })
954
+ })] })
955
+ ]
956
+ })
957
+ });
958
+ }
959
+ // src/lib/theme.ts
960
+ var THEMES = [
961
+ "system",
962
+ "light",
963
+ "dark"
964
+ ];
965
+ var themeSchema = z.enum(THEMES);
966
+ var getTheme = createServerFn({ method: "GET" }).handler(createSsrRpc("71c8991fc9c515da010713bdaa6b8c785cb3e21a16557175130babcfd659f0d8"));
967
+ var setTheme = createServerFn({ method: "POST" }).validator(themeSchema).handler(createSsrRpc("08e380ba074146180cab5cf970c6269c0fa27aca786f3aa0c9288cb27cccefbf"));
968
+ // src/components/McpSettings.tsx
969
+ var MASK = "•".repeat(43);
970
+ var COPY_CAPTION = {
971
+ idle: (caption) => caption,
972
+ copied: () => "Copied",
973
+ failed: () => "Copy failed"
974
+ };
975
+ function CopyButton({ text, label, noun, caption, announce }) {
976
+ const [state, setState] = useState("idle");
977
+ const copy = async () => {
978
+ try {
979
+ await navigator.clipboard.writeText(text);
980
+ setState("copied");
981
+ announce(`${noun} copied`);
982
+ } catch {
983
+ setState("failed");
984
+ announce(`${noun} copy failed`);
985
+ }
986
+ setTimeout(() => setState("idle"), 1500);
987
+ };
988
+ return /* @__PURE__ */ jsxs(Button$1, {
989
+ small: true,
990
+ icon: !caption,
991
+ "aria-label": state === "failed" ? `${label} failed` : label,
992
+ onClick: () => void copy(),
993
+ children: [/* @__PURE__ */ jsx(Copy, {}), caption ? COPY_CAPTION[state](caption) : null]
994
+ });
995
+ }
996
+ function McpSettings() {
997
+ const queryClient = useQueryClient();
998
+ const tokenQuery = useQuery(mcpTokenQuery());
999
+ const token = tokenQuery.data;
1000
+ const [announcement, setAnnouncement] = useState("");
1001
+ const announce = (message) => {
1002
+ setAnnouncement(message);
1003
+ setTimeout(() => setAnnouncement(""), 1500);
1004
+ };
1005
+ const [revealed, setRevealed] = useState(false);
1006
+ const [confirming, setConfirming] = useState(false);
1007
+ const regenerate = useMutation({
1008
+ mutationFn: () => regenerateMcpToken(),
1009
+ onSuccess: (next) => queryClient.setQueryData(mcpTokenQuery().queryKey, next)
1010
+ });
1011
+ const url = typeof window === "undefined" ? "/mcp" : `${window.location.origin}/mcp`;
1012
+ const command = (secret) => `claude mcp add --transport http db-player ${url} --header "Authorization: Bearer ${secret}"`;
1013
+ return /* @__PURE__ */ jsxs("section", {
1014
+ "aria-labelledby": "mcp",
1015
+ className: "x78zum5 xdt5ytf x1n8io78 x6mll90",
1016
+ children: [
1017
+ /* @__PURE__ */ jsx("h3", {
1018
+ id: "mcp",
1019
+ className: "x1ghz6dp xvj2dgp x1s688f",
1020
+ children: "Agent access (MCP)"
1021
+ }),
1022
+ /* @__PURE__ */ jsx("div", {
1023
+ role: "status",
1024
+ "aria-live": "polite",
1025
+ className: "x10l6tqk x1i1rx1s xjm9jq1 xb3r6kr x1hyvwdk xuxw1ft",
1026
+ children: announcement
1027
+ }),
1028
+ /* @__PURE__ */ jsx("p", {
1029
+ className: "x1ghz6dp x9otpla xqtwvag x1tviuuy x1evy7pa",
1030
+ children: "Lets AI agents list your connections and run read-only queries. Writes, DDL and multiple statements are refused. For production, use a connection whose database user is read-only."
1031
+ }),
1032
+ /* @__PURE__ */ jsxs("div", {
1033
+ className: "x78zum5 xdt5ytf x1mbf7id",
1034
+ children: [/* @__PURE__ */ jsx("span", {
1035
+ className: "xqtwvag x1tviuuy",
1036
+ children: "URL"
1037
+ }), /* @__PURE__ */ jsxs("div", {
1038
+ className: "x78zum5 x1cy8zhl xbbb4fa",
1039
+ children: [/* @__PURE__ */ jsx("code", {
1040
+ "data-testid": "mcp-url",
1041
+ className: "x98rzlu xeuugli xifx94n xu7byfh xxb57xf xqtwvag x1evy7pa xgwgzc4 x122w267 xvjf9rd xmkeg23 x1y0btm7 xpa2s8g xj0a0fe x126k92a xvrbanz",
1042
+ children: url
1043
+ }), /* @__PURE__ */ jsx(CopyButton, {
1044
+ text: url,
1045
+ label: "Copy URL",
1046
+ noun: "URL",
1047
+ announce
1048
+ })]
1049
+ })]
1050
+ }),
1051
+ /* @__PURE__ */ jsxs("div", {
1052
+ className: "x78zum5 xdt5ytf x1mbf7id",
1053
+ children: [/* @__PURE__ */ jsx("span", {
1054
+ className: "xqtwvag x1tviuuy",
1055
+ children: "Token"
1056
+ }), /* @__PURE__ */ jsxs("div", {
1057
+ className: "x78zum5 x1cy8zhl xbbb4fa",
1058
+ children: [
1059
+ /* @__PURE__ */ jsx("code", {
1060
+ "data-testid": "mcp-token",
1061
+ className: "x98rzlu xeuugli xifx94n xu7byfh xxb57xf xqtwvag x1evy7pa xgwgzc4 x122w267 xvjf9rd xmkeg23 x1y0btm7 xpa2s8g xj0a0fe x126k92a xvrbanz",
1062
+ children: token ? revealed ? token : MASK : tokenQuery.isError ? "Token unavailable" : "Loading…"
1063
+ }),
1064
+ !token && tokenQuery.isError ? /* @__PURE__ */ jsx(Button$1, {
1065
+ small: true,
1066
+ onClick: () => void tokenQuery.refetch(),
1067
+ disabled: tokenQuery.isFetching,
1068
+ children: "Retry"
1069
+ }) : null,
1070
+ /* @__PURE__ */ jsx(Button$1, {
1071
+ small: true,
1072
+ icon: true,
1073
+ "aria-label": revealed ? "Hide token" : "Show token",
1074
+ disabled: !token,
1075
+ onClick: () => setRevealed(!revealed),
1076
+ children: revealed ? /* @__PURE__ */ jsx(EyeOff, {}) : /* @__PURE__ */ jsx(Eye, {})
1077
+ }),
1078
+ token ? /* @__PURE__ */ jsx(CopyButton, {
1079
+ text: token,
1080
+ label: "Copy token",
1081
+ noun: "Token",
1082
+ announce
1083
+ }) : null
1084
+ ]
1085
+ })]
1086
+ }),
1087
+ /* @__PURE__ */ jsxs("div", {
1088
+ className: "x78zum5 xdt5ytf x1mbf7id",
1089
+ children: [
1090
+ /* @__PURE__ */ jsx("span", {
1091
+ className: "xqtwvag x1tviuuy",
1092
+ children: "Add to Claude Code"
1093
+ }),
1094
+ /* @__PURE__ */ jsx("code", {
1095
+ "data-testid": "mcp-command",
1096
+ className: "x98rzlu xeuugli xifx94n xu7byfh xxb57xf xqtwvag x1evy7pa xgwgzc4 x122w267 xvjf9rd xmkeg23 x1y0btm7 xpa2s8g xj0a0fe x126k92a xvrbanz",
1097
+ children: token ? command(revealed ? token : MASK).replace(" --header", " \\\n --header") : tokenQuery.isError ? "Token unavailable" : "Loading…"
1098
+ }),
1099
+ /* @__PURE__ */ jsxs("div", {
1100
+ className: "x78zum5 xbbb4fa",
1101
+ children: [token ? /* @__PURE__ */ jsx(CopyButton, {
1102
+ text: command(token),
1103
+ label: "Copy command",
1104
+ noun: "Command",
1105
+ caption: "Copy command",
1106
+ announce
1107
+ }) : null, /* @__PURE__ */ jsx(Button$1, {
1108
+ small: true,
1109
+ variant: "danger",
1110
+ onClick: () => setConfirming(true),
1111
+ children: "Regenerate token"
1112
+ })]
1113
+ })
1114
+ ]
1115
+ }),
1116
+ confirming ? /* @__PURE__ */ jsx(ConfirmDialog, {
1117
+ title: "Regenerate token?",
1118
+ message: "Every existing client stops working until you give it the new token.",
1119
+ confirmLabel: "Regenerate",
1120
+ busy: regenerate.isPending,
1121
+ onCancel: () => setConfirming(false),
1122
+ onConfirm: async () => {
1123
+ await regenerate.mutateAsync();
1124
+ setConfirming(false);
1125
+ }
1126
+ }) : null
1127
+ ]
1128
+ });
1129
+ }
1130
+ // src/components/SettingsDialog.tsx
1131
+ var rootRoute = getRouteApi("__root__");
1132
+ var LABELS = {
1133
+ system: "System",
1134
+ light: "Light",
1135
+ dark: "Dark"
1136
+ };
1137
+ var _temp$3 = {
1138
+ kZKoxP: "x1ycjhwn",
1139
+ kaIpWk: "x18zih8k",
1140
+ kWkggS: "x1ezoecf",
1141
+ "$$css": true
1142
+ };
1143
+ var styles$5 = {
1144
+ preview: {
1145
+ kVAEAm: "x1n2onr6",
1146
+ kZKoxP: "x1peatla",
1147
+ kVQacm: "xb3r6kr",
1148
+ kaIpWk: "xvjf9rd",
1149
+ kMzoRj: "xmkeg23",
1150
+ ksu8eU: "x1y0btm7",
1151
+ kVAM5u: "xpa2s8g",
1152
+ kWkggS: "x185or1n",
1153
+ $$css: true
1154
+ },
1155
+ half: {
1156
+ kVAEAm: "x10l6tqk",
1157
+ kpwlN0: "x10a8y8t",
1158
+ $$css: true
1159
+ },
1160
+ left: {
1161
+ kz4h6p: "x1otnbfm",
1162
+ $$css: true
1163
+ },
1164
+ right: {
1165
+ kz4h6p: "xfh1k12",
1166
+ $$css: true
1167
+ },
1168
+ bar: (w) => [
1169
+ _temp$3,
1170
+ {
1171
+ kzqmXN: w != null ? "x5lhr3w" : w,
1172
+ $$css: true
1173
+ },
1174
+ { "--x-width": ((val) => typeof val === "number" ? val + "px" : val != null ? val : void 0)(w) }
1175
+ ],
1176
+ barStrong: {
1177
+ kWkggS: "x152q98y",
1178
+ $$css: true
1179
+ }
1180
+ };
1181
+ function Mini() {
1182
+ return /* @__PURE__ */ jsxs("div", {
1183
+ className: "x78zum5 x5yr21d x185or1n",
1184
+ children: [/* @__PURE__ */ jsxs("div", {
1185
+ className: "xdqdh73 xgwgzc4 xs1s249 x32b0ac xl7466b x1vvogim x1xijbu4 x78zum5 xdt5ytf x1jnr06f",
1186
+ children: [
1187
+ /* @__PURE__ */ jsx("div", { ...stylex.props(styles$5.bar("80%"), styles$5.barStrong) }),
1188
+ /* @__PURE__ */ jsx("div", { ...stylex.props(styles$5.bar("60%")) }),
1189
+ /* @__PURE__ */ jsx("div", { ...stylex.props(styles$5.bar("70%")) })
1190
+ ]
1191
+ }), /* @__PURE__ */ jsxs("div", {
1192
+ className: "x98rzlu xe8ttls x78zum5 xdt5ytf x1nejdyq",
1193
+ children: [
1194
+ /* @__PURE__ */ jsx("div", { ...stylex.props(styles$5.bar("50%"), styles$5.barStrong) }),
1195
+ /* @__PURE__ */ jsx("div", { ...stylex.props(styles$5.bar("90%")) }),
1196
+ /* @__PURE__ */ jsx("div", { ...stylex.props(styles$5.bar("75%")) }),
1197
+ /* @__PURE__ */ jsx("div", { className: "xdk7pt xgd8bvy x18zih8k xq83s4c xr1yuqi" })
1198
+ ]
1199
+ })]
1200
+ });
1201
+ }
1202
+ function Preview({ mode }) {
1203
+ if (mode === "system") return /* @__PURE__ */ jsxs("div", {
1204
+ className: "x1n2onr6 x1peatla xb3r6kr xvjf9rd xmkeg23 x1y0btm7 xpa2s8g x185or1n",
1205
+ children: [/* @__PURE__ */ jsx("div", {
1206
+ ...stylex.props(styles$5.half, styles$5.left, lightTheme),
1207
+ children: /* @__PURE__ */ jsx(Mini, {})
1208
+ }), /* @__PURE__ */ jsx("div", {
1209
+ ...stylex.props(styles$5.half, styles$5.right, darkTheme),
1210
+ children: /* @__PURE__ */ jsx(Mini, {})
1211
+ })]
1212
+ });
1213
+ return /* @__PURE__ */ jsx("div", {
1214
+ ...stylex.props(styles$5.preview, mode === "light" ? lightTheme : darkTheme),
1215
+ children: /* @__PURE__ */ jsx(Mini, {})
1216
+ });
1217
+ }
1218
+ function SettingsDialog({ onClose }) {
1219
+ const { theme } = rootRoute.useLoaderData();
1220
+ const router = useRouter();
1221
+ const choose = async (next) => {
1222
+ await setTheme({ data: next });
1223
+ await router.invalidate();
1224
+ };
1225
+ return /* @__PURE__ */ jsxs(Dialog$1, {
1226
+ open: true,
1227
+ onOpenChange: (open) => !open && onClose(),
1228
+ title: "Settings",
1229
+ wide: true,
1230
+ children: [
1231
+ /* @__PURE__ */ jsxs("section", {
1232
+ "aria-labelledby": "appearance",
1233
+ className: "x78zum5 xdt5ytf x1n8io78",
1234
+ children: [
1235
+ /* @__PURE__ */ jsx("h3", {
1236
+ id: "appearance",
1237
+ className: "x1ghz6dp xvj2dgp x1s688f",
1238
+ children: "Appearance"
1239
+ }),
1240
+ /* @__PURE__ */ jsx("p", {
1241
+ className: "x1ghz6dp x9otpla xqtwvag x1tviuuy",
1242
+ children: "Choose how db-player looks. System follows your OS."
1243
+ }),
1244
+ /* @__PURE__ */ jsx(RadioGroup, {
1245
+ value: theme,
1246
+ onValueChange: (v) => void choose(v),
1247
+ "aria-label": "Theme",
1248
+ className: "xrvj5dj x1wlfl8j x1n8io78",
1249
+ children: THEMES.map((t) => /* @__PURE__ */ jsxs(Radio.Root, {
1250
+ value: t,
1251
+ "aria-label": LABELS[t],
1252
+ className: "x78zum5 xdt5ytf xbbb4fa x1ruevkc x1o95cbt xgwgzc4 x122w267 xr11lhz xmkeg23 x1y0btm7 x4s12hp x1632hs x1cej5sy x1gnnqk1 xot0yp x1ypdohk x1a2a7pz x12lrzb7 x1hl8ikr x9vp5wv x1qfwjxi x6setmy x1c071of xk4oym4",
1253
+ children: [/* @__PURE__ */ jsx(Preview, { mode: t }), /* @__PURE__ */ jsx("span", {
1254
+ className: "xqtwvag xk50ysn x2b8uid",
1255
+ children: LABELS[t]
1256
+ })]
1257
+ }, t))
1258
+ })
1259
+ ]
1260
+ }),
1261
+ /* @__PURE__ */ jsx(McpSettings, {}),
1262
+ /* @__PURE__ */ jsx(DialogFooter, { children: /* @__PURE__ */ jsx(Button$1, {
1263
+ onClick: onClose,
1264
+ children: "Done"
1265
+ }) })
1266
+ ]
1267
+ });
1268
+ }
1269
+ // src/components/ui/Kbd.tsx
1270
+ function Kbd({ children }) {
1271
+ return /* @__PURE__ */ jsx("kbd", {
1272
+ className: "x3nfvp2 x6s0dn4 xlup9mm xfn1xvj x134rb1s x1j6dyjg xk50ysn x1tviuuy xn7wf0q xmkeg23 x1y0btm7 x4s12hp",
1273
+ children
1274
+ });
1275
+ }
1276
+ // src/components/ui/Menu.tsx
1277
+ var styles$4 = { surface: {
1278
+ k7Eaqz: "x57cstt",
1279
+ $$css: true
1280
+ } };
1281
+ function Entries({ entries, parts }) {
1282
+ return entries.map((entry, i) => {
1283
+ if (entry.type === "separator") return /* @__PURE__ */ jsx(parts.Separator, { className: "xjm9jq1 xg9jes1 xr6ywxm x14i5uus" }, i);
1284
+ if (entry.type === "submenu") return /* @__PURE__ */ jsxs(parts.SubmenuRoot, { children: [/* @__PURE__ */ jsxs(parts.SubmenuTrigger, {
1285
+ className: "x78zum5 x6s0dn4 x1qughib x1v2ro7d x1jgo0hl xdqyycr xqtwvag x122w267 xn7wf0q xt0e3qv x1a2a7pz x87ps6o xjbqb8w x1gjw99y x2e3pq1",
1286
+ children: [entry.label, /* @__PURE__ */ jsx("span", {
1287
+ className: "x1tviuuy x78zum5",
1288
+ children: /* @__PURE__ */ jsx(ChevronRight, {})
1289
+ })]
1290
+ }), /* @__PURE__ */ jsx(parts.Portal, { children: /* @__PURE__ */ jsx(parts.Positioner, {
1291
+ sideOffset: 4,
1292
+ ...stylex.props(popup.positioner),
1293
+ children: /* @__PURE__ */ jsx(parts.Popup, {
1294
+ ...stylex.props(popup.surface, styles$4.surface),
1295
+ children: /* @__PURE__ */ jsx(Entries, {
1296
+ entries: entry.entries,
1297
+ parts
1298
+ })
1299
+ })
1300
+ }) })] }, `${i}:${entry.label}`);
1301
+ return /* @__PURE__ */ jsxs(parts.Item, {
1302
+ onClick: entry.onSelect,
1303
+ ...{
1304
+ 0: { className: "x78zum5 x6s0dn4 x1qughib x1v2ro7d x1jgo0hl xdqyycr xqtwvag x122w267 xn7wf0q xt0e3qv x1a2a7pz x87ps6o xjbqb8w x1gjw99y x2e3pq1" },
1305
+ 1: { className: "x78zum5 x6s0dn4 x1qughib x1v2ro7d x1jgo0hl xdqyycr xqtwvag xn7wf0q xt0e3qv x1a2a7pz x87ps6o xjbqb8w x1gjw99y x2e3pq1 xb0vdwp" }
1306
+ }[!!entry.danger << 0],
1307
+ children: [entry.label, entry.hint ? /* @__PURE__ */ jsx("span", {
1308
+ className: "x1tviuuy",
1309
+ children: entry.hint
1310
+ }) : null]
1311
+ }, `${i}:${entry.label}`);
1312
+ });
1313
+ }
1314
+ function DropdownMenu({ trigger, entries }) {
1315
+ return /* @__PURE__ */ jsxs(Menu.Root, { children: [/* @__PURE__ */ jsx(Menu.Trigger, { render: trigger }), /* @__PURE__ */ jsx(Menu.Portal, { children: /* @__PURE__ */ jsx(Menu.Positioner, {
1316
+ align: "start",
1317
+ sideOffset: 4,
1318
+ ...stylex.props(popup.positioner),
1319
+ children: /* @__PURE__ */ jsx(Menu.Popup, {
1320
+ ...stylex.props(popup.surface, styles$4.surface),
1321
+ children: /* @__PURE__ */ jsx(Entries, {
1322
+ entries,
1323
+ parts: Menu
1324
+ })
1325
+ })
1326
+ }) })] });
1327
+ }
1328
+ function ContextMenuArea({ entries, children }) {
1329
+ return /* @__PURE__ */ jsxs(ContextMenu.Root, { children: [/* @__PURE__ */ jsx(ContextMenu.Trigger, {
1330
+ className: "xjp7ctv",
1331
+ children
1332
+ }), /* @__PURE__ */ jsx(ContextMenu.Portal, { children: /* @__PURE__ */ jsx(ContextMenu.Positioner, {
1333
+ ...stylex.props(popup.positioner),
1334
+ children: /* @__PURE__ */ jsx(ContextMenu.Popup, {
1335
+ ...stylex.props(popup.surface, styles$4.surface),
1336
+ children: /* @__PURE__ */ jsx(Entries, {
1337
+ entries,
1338
+ parts: ContextMenu
1339
+ })
1340
+ })
1341
+ }) })] });
1342
+ }
1343
+ // src/components/ui/ResizeHandle.tsx
1344
+ var KEY_STEP = 16;
1345
+ /** Vertical drag handle on the right edge of a `position: relative` panel. */
1346
+ function ResizeHandle({ label, value, min, max, defaultValue, onChange }) {
1347
+ const [drag, setDrag] = useState(null);
1348
+ const [focused, setFocused] = useState(false);
1349
+ const clamp = (w) => Math.min(max, Math.max(min, Math.round(w)));
1350
+ const onPointerDown = (e) => {
1351
+ if (e.button !== 0 || drag) return;
1352
+ e.currentTarget.setPointerCapture(e.pointerId);
1353
+ setDrag({
1354
+ startX: e.clientX,
1355
+ startWidth: value
1356
+ });
1357
+ document.body.style.cursor = "col-resize";
1358
+ document.body.style.userSelect = "none";
1359
+ };
1360
+ const onPointerMove = (e) => {
1361
+ if (drag) onChange(clamp(drag.startWidth + e.clientX - drag.startX), false);
1362
+ };
1363
+ const end = (e) => {
1364
+ if (!drag) return;
1365
+ if (e.currentTarget.hasPointerCapture(e.pointerId)) e.currentTarget.releasePointerCapture(e.pointerId);
1366
+ document.body.style.cursor = "";
1367
+ document.body.style.userSelect = "";
1368
+ setDrag(null);
1369
+ onChange(value, true);
1370
+ };
1371
+ const onKeyDown = (e) => {
1372
+ const step = e.key === "ArrowLeft" ? -16 : e.key === "ArrowRight" ? KEY_STEP : 0;
1373
+ if (step === 0) return;
1374
+ e.preventDefault();
1375
+ onChange(clamp(value + step), true);
1376
+ };
1377
+ return /* @__PURE__ */ jsx("div", {
1378
+ role: "separator",
1379
+ "aria-orientation": "vertical",
1380
+ "aria-label": label,
1381
+ "aria-valuenow": value,
1382
+ "aria-valuemin": min,
1383
+ "aria-valuemax": max,
1384
+ tabIndex: 0,
1385
+ onPointerDown,
1386
+ onPointerMove,
1387
+ onPointerUp: end,
1388
+ onPointerCancel: end,
1389
+ onLostPointerCapture: end,
1390
+ onDoubleClick: () => onChange(defaultValue, true),
1391
+ onKeyDown,
1392
+ onFocus: (e) => setFocused(e.currentTarget.matches(":focus-visible")),
1393
+ onBlur: () => setFocused(false),
1394
+ className: "x10l6tqk x13vifvy x1ey2m1c xzkxjte x1wc42o8 x1u8a7rm xicojor x5ve5x3 x1a2a7pz x78zum5 xl56j7k",
1395
+ children: /* @__PURE__ */ jsx("div", { ...{
1396
+ 0: { className: "xfo62xy x5yr21d xjbqb8w xvs4sct x15406qy x1qfwjxi x6setmy" },
1397
+ 2: { className: "xfo62xy x5yr21d x15406qy x1qfwjxi x6setmy x152q98y" },
1398
+ 1: { className: "xfo62xy x5yr21d x15406qy x1qfwjxi x6setmy xq83s4c" },
1399
+ 3: { className: "xfo62xy x5yr21d x15406qy x1qfwjxi x6setmy xq83s4c" }
1400
+ }[!!drag << 1 | !!focused << 0] })
1401
+ });
1402
+ }
1403
+ // src/components/ui/Tooltip.tsx
1404
+ /** One provider for the app, so a tooltip opened right after another shows instantly. */
1405
+ function TooltipProvider({ children }) {
1406
+ return /* @__PURE__ */ jsx(Tooltip.Provider, {
1407
+ delay: 400,
1408
+ closeDelay: 0,
1409
+ children
1410
+ });
1411
+ }
1412
+ function Tooltip$1({ label, hint, children }) {
1413
+ return /* @__PURE__ */ jsxs(Tooltip.Root, { children: [/* @__PURE__ */ jsx(Tooltip.Trigger, { render: children }), /* @__PURE__ */ jsx(Tooltip.Portal, { children: /* @__PURE__ */ jsx(Tooltip.Positioner, {
1414
+ sideOffset: 6,
1415
+ className: "x6y048q",
1416
+ children: /* @__PURE__ */ jsxs(Tooltip.Popup, {
1417
+ className: "x78zum5 x6s0dn4 x167g77z x12ulsxz xdqyycr xqtwvag x14fh20 x122w267 xvjf9rd xmkeg23 x1y0btm7 x4s12hp x5kdpwh x1jkqq1h xctpfpq x1hc1fzr x1hw7zpd xiyypsa x3oybdh x1qdyiyv x14up28i x6o7n8i x19jr8i9 x1fdgx8g x1ya1k7h x6setmy",
1418
+ children: [label, hint]
1419
+ })
1420
+ }) })] });
1421
+ }
1422
+ // src/components/ui/Num.tsx
1423
+ function Num({ children }) {
1424
+ return /* @__PURE__ */ jsx("span", {
1425
+ className: "xxb57xf xss6m8b",
1426
+ children
1427
+ });
1428
+ }
1429
+ // src/components/Sidebar.tsx
1430
+ var _temp$2 = {
1431
+ k1K539: "x1k9oyp",
1432
+ kmVPX3: "x13bz5fa",
1433
+ kaIpWk: "xr11lhz",
1434
+ kWkggS: "xxhzbed",
1435
+ "$$css": true
1436
+ };
1437
+ var _temp2$1 = {
1438
+ kzqmXN: "x1xc55vz",
1439
+ kZKoxP: "xdk7pt",
1440
+ kaIpWk: "x16rqkct",
1441
+ kmuXW: "x2lah0s",
1442
+ "$$css": true
1443
+ };
1444
+ var styles$3 = {
1445
+ sidebar: {
1446
+ kVAEAm: "x1n2onr6",
1447
+ kmuXW: "x2lah0s",
1448
+ k1xSpc: "x78zum5",
1449
+ kXwgrk: "xdt5ytf",
1450
+ kZKoxP: "x5yr21d",
1451
+ kWkggS: "xgwgzc4",
1452
+ kWqL5O: "xs1s249",
1453
+ kSWEuD: "x32b0ac",
1454
+ k26BEO: "xl7466b",
1455
+ $$css: true
1456
+ },
1457
+ width: (w) => [{
1458
+ kzqmXN: w != null ? "x5lhr3w" : w,
1459
+ $$css: true
1460
+ }, { "--x-width": ((val) => typeof val === "number" ? val + "px" : val != null ? val : void 0)(w) }],
1461
+ group: (hue) => [
1462
+ _temp$2,
1463
+ {
1464
+ "--xm54oyi": hue != null ? "xyynxod" : hue,
1465
+ $$css: true
1466
+ },
1467
+ { "--x---xm54oyi": hue != null ? hue : void 0 }
1468
+ ],
1469
+ dot: (c) => [
1470
+ _temp2$1,
1471
+ {
1472
+ kWkggS: c != null ? "xl8spv7" : c,
1473
+ $$css: true
1474
+ },
1475
+ { "--x-backgroundColor": c != null ? c : void 0 }
1476
+ ]
1477
+ };
1478
+ function Sidebar() {
1479
+ const folders = useQuery(foldersQuery());
1480
+ const connections = useQuery(connectionsQuery());
1481
+ const m = useSidebarMutations();
1482
+ const navigate = useNavigate();
1483
+ const params = useParams({ strict: false });
1484
+ const explorerId = useExplorerConnectionId();
1485
+ const { layout, update } = useLayout();
1486
+ const [dialog, setDialog] = useState(null);
1487
+ const [collapsed, setCollapsed] = useState(/* @__PURE__ */ new Set());
1488
+ useHotkey("Mod+,", () => setDialog({ type: "settings" }));
1489
+ const folderList = folders.data ?? [];
1490
+ const connList = connections.data ?? [];
1491
+ const ungrouped = connList.filter((c) => c.folderId === null || !folderList.some((f) => f.id === c.folderId));
1492
+ const toggle = (id) => {
1493
+ const next = new Set(collapsed);
1494
+ if (next.has(id)) next.delete(id);
1495
+ else next.add(id);
1496
+ setCollapsed(next);
1497
+ };
1498
+ const connectionEntries = (c) => [
1499
+ {
1500
+ label: "Edit...",
1501
+ onSelect: () => setDialog({
1502
+ type: "connection",
1503
+ connection: c
1504
+ })
1505
+ },
1506
+ {
1507
+ type: "submenu",
1508
+ label: "Move to folder",
1509
+ entries: [{
1510
+ label: "Ungrouped",
1511
+ onSelect: () => m.moveConnection.mutate({ data: {
1512
+ id: c.id,
1513
+ folderId: null
1514
+ } })
1515
+ }, ...folderList.map((f) => ({
1516
+ label: f.name,
1517
+ onSelect: () => m.moveConnection.mutate({ data: {
1518
+ id: c.id,
1519
+ folderId: f.id
1520
+ } })
1521
+ }))]
1522
+ },
1523
+ { type: "separator" },
1524
+ {
1525
+ label: "Delete",
1526
+ danger: true,
1527
+ onSelect: () => setDialog({
1528
+ type: "delete-connection",
1529
+ connection: c
1530
+ })
1531
+ }
1532
+ ];
1533
+ const folderEntries = (f) => [
1534
+ {
1535
+ label: "New connection here...",
1536
+ onSelect: () => setDialog({
1537
+ type: "connection",
1538
+ folderId: f.id
1539
+ })
1540
+ },
1541
+ {
1542
+ label: "Rename / recolor...",
1543
+ onSelect: () => setDialog({
1544
+ type: "folder",
1545
+ folder: f
1546
+ })
1547
+ },
1548
+ { type: "separator" },
1549
+ {
1550
+ label: "Delete folder",
1551
+ danger: true,
1552
+ onSelect: () => setDialog({
1553
+ type: "delete-folder",
1554
+ folder: f
1555
+ })
1556
+ }
1557
+ ];
1558
+ const renderConnection = (tinted) => (c) => /* @__PURE__ */ jsx(ContextMenuArea, {
1559
+ entries: connectionEntries(c),
1560
+ children: /* @__PURE__ */ jsxs("div", {
1561
+ ...{
1562
+ 0: { className: "x1n2onr6 x78zum5 x6s0dn4 x1jgo0hl xvjf9rd xjbqb8w x1sxlxsz x15406qy x19jr8i9 x6setmy x-default-marker" },
1563
+ 4: { className: "x1n2onr6 x78zum5 x6s0dn4 x1jgo0hl xvjf9rd x15406qy x19jr8i9 x6setmy x1nke8ei x-default-marker" },
1564
+ 2: { className: "x1n2onr6 x78zum5 x6s0dn4 x1jgo0hl xvjf9rd x15406qy x19jr8i9 x6setmy xjbqb8w x1jw1774 x-default-marker" },
1565
+ 6: { className: "x1n2onr6 x78zum5 x6s0dn4 x1jgo0hl xvjf9rd x15406qy x19jr8i9 x6setmy xjbqb8w x1jw1774 x-default-marker" },
1566
+ 1: { className: "x1n2onr6 x78zum5 x6s0dn4 x1jgo0hl xvjf9rd x15406qy x19jr8i9 x6setmy x11lsgwc x-default-marker" },
1567
+ 5: { className: "x1n2onr6 x78zum5 x6s0dn4 x1jgo0hl xvjf9rd x15406qy x19jr8i9 x6setmy x11lsgwc x-default-marker" },
1568
+ 3: { className: "x1n2onr6 x78zum5 x6s0dn4 x1jgo0hl xvjf9rd x15406qy x19jr8i9 x6setmy x11lsgwc x-default-marker" },
1569
+ 7: { className: "x1n2onr6 x78zum5 x6s0dn4 x1jgo0hl xvjf9rd x15406qy x19jr8i9 x6setmy x11lsgwc x-default-marker" }
1570
+ }[!!(explorerId === c.id) << 2 | !!tinted << 1 | !!(tinted && explorerId === c.id) << 0],
1571
+ children: [/* @__PURE__ */ jsxs(Link, {
1572
+ to: "/c/$connectionId",
1573
+ params: { connectionId: c.id },
1574
+ className: "x78zum5 x6s0dn4 xbbb4fa x98rzlu xeuugli x5yr21d x1x7g8rv xy13l1i xvjf9rd x122w267 x1hl2dhg xvj2dgp x1a2a7pz xn0s4wz x1g40iwv",
1575
+ children: [
1576
+ /* @__PURE__ */ jsx("span", {
1577
+ className: "x78zum5 x1tviuuy",
1578
+ children: /* @__PURE__ */ jsx(Database, {})
1579
+ }),
1580
+ /* @__PURE__ */ jsx("span", {
1581
+ className: "x98rzlu xeuugli xb3r6kr xlyipyv xuxw1ft",
1582
+ children: c.name
1583
+ }),
1584
+ /* @__PURE__ */ jsx("span", {
1585
+ className: "x2lah0s x1j6dyjg x1tviuuy x1hc1fzr xpr5m1m xzo1hb2 x11fdr6z",
1586
+ children: DB_KIND_INFO[c.kind].label
1587
+ })
1588
+ ]
1589
+ }), /* @__PURE__ */ jsx("div", {
1590
+ className: "x10l6tqk x3lmqil xs7f9wi x1hc1fzr x4fpvau x7stcvd x1j36eff x1apysje",
1591
+ children: /* @__PURE__ */ jsx(DropdownMenu, {
1592
+ trigger: /* @__PURE__ */ jsx(Button$1, {
1593
+ variant: "ghost",
1594
+ icon: true,
1595
+ small: true,
1596
+ "aria-label": `Actions for ${c.name}`,
1597
+ children: /* @__PURE__ */ jsx(Dots, {})
1598
+ }),
1599
+ entries: connectionEntries(c)
1600
+ })
1601
+ })]
1602
+ })
1603
+ }, c.id);
1604
+ const folderHeader = (id, label, count, dot, menu, tinted = false) => /* @__PURE__ */ jsxs("div", {
1605
+ ...{
1606
+ 0: { className: "x1n2onr6 x78zum5 x6s0dn4 x1jgo0hl xvjf9rd xjbqb8w x1sxlxsz x15406qy x19jr8i9 x6setmy x-default-marker" },
1607
+ 1: { className: "x1n2onr6 x78zum5 x6s0dn4 x1jgo0hl xvjf9rd x15406qy x19jr8i9 x6setmy xjbqb8w x1jw1774 x-default-marker" }
1608
+ }[!!tinted << 0],
1609
+ children: [/* @__PURE__ */ jsxs("button", {
1610
+ type: "button",
1611
+ "aria-expanded": !collapsed.has(id),
1612
+ className: "x78zum5 x6s0dn4 x17d4w8g x98rzlu xeuugli x5yr21d x1xijbu4 xc342km xvjf9rd xjbqb8w x1ypdohk xdpxx8g x122w267 xvj2dgp xk50ysn x87ps6o x1a2a7pz xn0s4wz x1g40iwv",
1613
+ onClick: () => toggle(id),
1614
+ children: [
1615
+ /* @__PURE__ */ jsx("span", {
1616
+ ...{
1617
+ 0: { className: "x78zum5 x1tviuuy x7p49u4 x11xpdln x19jr8i9 x6setmy" },
1618
+ 1: { className: "x78zum5 x1tviuuy x11xpdln x19jr8i9 x6setmy x1iffjtl" }
1619
+ }[!!!collapsed.has(id) << 0],
1620
+ children: /* @__PURE__ */ jsx(ChevronRight, {})
1621
+ }),
1622
+ dot ? /* @__PURE__ */ jsx("span", { ...stylex.props(styles$3.dot(dot)) }) : null,
1623
+ /* @__PURE__ */ jsx("span", {
1624
+ className: "x98rzlu xeuugli xb3r6kr xlyipyv xuxw1ft",
1625
+ children: label
1626
+ }),
1627
+ /* @__PURE__ */ jsx("span", {
1628
+ className: "x1tviuuy xqtwvag xo1l8bm x1hc1fzr xpr5m1m xzo1hb2 x11fdr6z",
1629
+ children: /* @__PURE__ */ jsx(Num, { children: count })
1630
+ })
1631
+ ]
1632
+ }), menu]
1633
+ });
1634
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1635
+ layout.sidebarOpen ? /* @__PURE__ */ jsxs("aside", {
1636
+ ...stylex.props(styles$3.sidebar, styles$3.width(layout.sidebarWidth)),
1637
+ children: [
1638
+ /* @__PURE__ */ jsxs("div", {
1639
+ className: "x78zum5 xbbb4fa xbhnzry x2lah0s",
1640
+ children: [/* @__PURE__ */ jsx("div", {
1641
+ className: "x98rzlu",
1642
+ children: /* @__PURE__ */ jsxs(Button$1, {
1643
+ variant: "primary",
1644
+ fill: true,
1645
+ onClick: () => setDialog({ type: "connection" }),
1646
+ children: [/* @__PURE__ */ jsx(Plus, {}), " New connection"]
1647
+ })
1648
+ }), /* @__PURE__ */ jsx(Tooltip$1, {
1649
+ label: "New folder",
1650
+ children: /* @__PURE__ */ jsx(Button$1, {
1651
+ icon: true,
1652
+ "aria-label": "New folder",
1653
+ onClick: () => setDialog({ type: "folder" }),
1654
+ children: /* @__PURE__ */ jsx(FolderPlus, {})
1655
+ })
1656
+ })]
1657
+ }),
1658
+ /* @__PURE__ */ jsxs("div", {
1659
+ className: "x98rzlu x1odjw0f x1ik00rn xaxbvzv",
1660
+ children: [
1661
+ connections.isError ? /* @__PURE__ */ jsx("div", {
1662
+ className: "xmjl1bo xqtwvag xb0vdwp",
1663
+ children: "Could not load connections."
1664
+ }) : null,
1665
+ connList.length === 0 && folderList.length === 0 ? /* @__PURE__ */ jsx("div", {
1666
+ className: "x1a2dy25 xqtwvag x1tviuuy x1evy7pa",
1667
+ children: "No connections yet. Create one to start browsing a database."
1668
+ }) : null,
1669
+ folderList.map((f) => {
1670
+ const inFolder = connList.filter((c) => c.folderId === f.id);
1671
+ return /* @__PURE__ */ jsxs("div", {
1672
+ ...stylex.props(styles$3.group(FOLDER_COLORS[f.color])),
1673
+ children: [/* @__PURE__ */ jsx(ContextMenuArea, {
1674
+ entries: folderEntries(f),
1675
+ children: folderHeader(f.id, f.name, inFolder.length, FOLDER_COLORS[f.color], /* @__PURE__ */ jsx("div", {
1676
+ className: "x10l6tqk x3lmqil xs7f9wi x1hc1fzr x4fpvau x7stcvd x1j36eff x1apysje",
1677
+ children: /* @__PURE__ */ jsx(DropdownMenu, {
1678
+ trigger: /* @__PURE__ */ jsx(Button$1, {
1679
+ variant: "ghost",
1680
+ icon: true,
1681
+ small: true,
1682
+ "aria-label": `Actions for folder ${f.name}`,
1683
+ children: /* @__PURE__ */ jsx(Dots, {})
1684
+ }),
1685
+ entries: folderEntries(f)
1686
+ })
1687
+ }), true)
1688
+ }), collapsed.has(f.id) ? null : inFolder.map(renderConnection(true))]
1689
+ }, f.id);
1690
+ }),
1691
+ ungrouped.length > 0 ? /* @__PURE__ */ jsxs("div", {
1692
+ className: "x13bz5fa",
1693
+ children: [folderHeader("__ungrouped__", "Ungrouped", ungrouped.length, null, null), collapsed.has("__ungrouped__") ? null : ungrouped.map(renderConnection(false))]
1694
+ }) : null
1695
+ ]
1696
+ }),
1697
+ /* @__PURE__ */ jsx("div", {
1698
+ className: "x2lah0s xbhnzry x178xt8z x13fuv20 x10gwal6",
1699
+ children: /* @__PURE__ */ jsxs("button", {
1700
+ type: "button",
1701
+ className: "x78zum5 x6s0dn4 xbbb4fa xh8yej3 x1jgo0hl x1xijbu4 xc342km xvjf9rd xjbqb8w x1sxlxsz x1tviuuy xvj2dgp x1ypdohk xdpxx8g x101pvzj x1qfwjxi x6setmy x1c071of xk4oym4 x1a2a7pz xn0s4wz x1g40iwv",
1702
+ onClick: () => setDialog({ type: "settings" }),
1703
+ children: [
1704
+ /* @__PURE__ */ jsx(Settings, {}),
1705
+ /* @__PURE__ */ jsx("span", {
1706
+ className: "x98rzlu xeuugli xb3r6kr xlyipyv xuxw1ft",
1707
+ children: "Settings"
1708
+ }),
1709
+ /* @__PURE__ */ jsx(Kbd, { children: "⌘," })
1710
+ ]
1711
+ })
1712
+ }),
1713
+ /* @__PURE__ */ jsx(ResizeHandle, {
1714
+ label: "Resize sidebar",
1715
+ value: layout.sidebarWidth,
1716
+ min: 180,
1717
+ max: 480,
1718
+ defaultValue: DEFAULT_LAYOUT.sidebarWidth,
1719
+ onChange: (w, commit) => update({ sidebarWidth: w }, commit)
1720
+ })
1721
+ ]
1722
+ }) : null,
1723
+ dialog?.type === "settings" ? /* @__PURE__ */ jsx(SettingsDialog, { onClose: () => setDialog(null) }) : null,
1724
+ dialog?.type === "folder" ? /* @__PURE__ */ jsx(FolderDialog, {
1725
+ folder: dialog.folder,
1726
+ onClose: () => setDialog(null)
1727
+ }) : null,
1728
+ dialog?.type === "connection" ? /* @__PURE__ */ jsx(ConnectionDialog, {
1729
+ connection: dialog.connection,
1730
+ folderId: dialog.folderId,
1731
+ folders: folderList,
1732
+ onClose: () => setDialog(null)
1733
+ }) : null,
1734
+ dialog?.type === "delete-folder" ? /* @__PURE__ */ jsx(ConfirmDialog, {
1735
+ title: "Delete folder",
1736
+ message: `Delete "${dialog.folder.name}"? Its connections move to Ungrouped.`,
1737
+ confirmLabel: "Delete folder",
1738
+ busy: m.deleteFolder.isPending,
1739
+ onCancel: () => setDialog(null),
1740
+ onConfirm: async () => {
1741
+ await m.deleteFolder.mutateAsync({ data: { id: dialog.folder.id } });
1742
+ setDialog(null);
1743
+ }
1744
+ }) : null,
1745
+ dialog?.type === "delete-connection" ? /* @__PURE__ */ jsx(ConfirmDialog, {
1746
+ title: "Delete connection",
1747
+ message: `Delete connection "${dialog.connection.name}"? The database itself is not touched.`,
1748
+ confirmLabel: "Delete connection",
1749
+ busy: m.deleteConnection.isPending,
1750
+ onCancel: () => setDialog(null),
1751
+ onConfirm: async () => {
1752
+ const id = dialog.connection.id;
1753
+ const workspace = await m.deleteConnection.mutateAsync({ data: { id } });
1754
+ setDialog(null);
1755
+ const tabGone = params.tabId && !workspace.tabs.some((t) => t.id === params.tabId);
1756
+ if (params.connectionId === id || tabGone) await navigate({ to: "/" });
1757
+ }
1758
+ }) : null
1759
+ ] });
1760
+ }
1761
+ // src/components/ui/Logo.tsx
1762
+ function Logo() {
1763
+ return /* @__PURE__ */ jsxs("svg", {
1764
+ viewBox: "0 0 64 64",
1765
+ "aria-hidden": "true",
1766
+ className: "x1lliihq x2lah0s x1kky2od xlup9mm",
1767
+ children: [
1768
+ /* @__PURE__ */ jsx("rect", {
1769
+ width: "64",
1770
+ height: "64",
1771
+ rx: "14",
1772
+ className: "x11lgzim"
1773
+ }),
1774
+ /* @__PURE__ */ jsx("rect", {
1775
+ x: "15",
1776
+ y: "11",
1777
+ width: "12",
1778
+ height: "6",
1779
+ rx: "3",
1780
+ fill: "#ef4444"
1781
+ }),
1782
+ /* @__PURE__ */ jsx("rect", {
1783
+ x: "15",
1784
+ y: "20",
1785
+ width: "24",
1786
+ height: "6",
1787
+ rx: "3",
1788
+ fill: "#f97316"
1789
+ }),
1790
+ /* @__PURE__ */ jsx("rect", {
1791
+ x: "15",
1792
+ y: "29",
1793
+ width: "36",
1794
+ height: "6",
1795
+ rx: "3",
1796
+ fill: "#eab308"
1797
+ }),
1798
+ /* @__PURE__ */ jsx("rect", {
1799
+ x: "15",
1800
+ y: "38",
1801
+ width: "24",
1802
+ height: "6",
1803
+ rx: "3",
1804
+ fill: "#22c55e"
1805
+ }),
1806
+ /* @__PURE__ */ jsx("rect", {
1807
+ x: "15",
1808
+ y: "47",
1809
+ width: "12",
1810
+ height: "6",
1811
+ rx: "3",
1812
+ fill: "#3b82f6"
1813
+ })
1814
+ ]
1815
+ });
1816
+ }
1817
+ // src/components/TopBar.tsx
1818
+ function TopBar() {
1819
+ const { layout, update } = useLayout();
1820
+ const toggle = () => update({ sidebarOpen: !layout.sidebarOpen });
1821
+ useHotkey("Mod+B", toggle);
1822
+ return /* @__PURE__ */ jsxs("header", {
1823
+ className: "x78zum5 x6s0dn4 xbbb4fa x5md06r xu7byfh x2lah0s xgwgzc4 xso031l x1q0q8m5 x1atvb72",
1824
+ children: [
1825
+ /* @__PURE__ */ jsx(Tooltip$1, {
1826
+ label: layout.sidebarOpen ? "Hide sidebar" : "Show sidebar",
1827
+ hint: /* @__PURE__ */ jsx(Kbd, { children: "⌘B" }),
1828
+ children: /* @__PURE__ */ jsx(Button$1, {
1829
+ variant: "ghost",
1830
+ icon: true,
1831
+ "aria-label": "Toggle sidebar",
1832
+ "aria-pressed": layout.sidebarOpen,
1833
+ onClick: toggle,
1834
+ children: /* @__PURE__ */ jsx(PanelLeft, { size: 16 })
1835
+ })
1836
+ }),
1837
+ /* @__PURE__ */ jsx(Logo, {}),
1838
+ /* @__PURE__ */ jsx("span", {
1839
+ className: "xvj2dgp x1s688f xtiv8zk",
1840
+ children: "db-player"
1841
+ })
1842
+ ]
1843
+ });
1844
+ }
1845
+ // src/components/Explorer.tsx
1846
+ var styles$2 = {
1847
+ explorer: {
1848
+ kVAEAm: "x1n2onr6",
1849
+ kmuXW: "x2lah0s",
1850
+ k1xSpc: "x78zum5",
1851
+ kXwgrk: "xdt5ytf",
1852
+ kZKoxP: "x5yr21d",
1853
+ kWkggS: "x185or1n",
1854
+ kWqL5O: "xs1s249",
1855
+ kSWEuD: "x32b0ac",
1856
+ k26BEO: "xl7466b",
1857
+ $$css: true
1858
+ },
1859
+ width: (w) => [{
1860
+ kzqmXN: w != null ? "x5lhr3w" : w,
1861
+ $$css: true
1862
+ }, { "--x-width": ((val) => typeof val === "number" ? val + "px" : val != null ? val : void 0)(w) }]
1863
+ };
1864
+ function Tables({ connectionId, database, filter, activeTable }) {
1865
+ const actions = useWorkspaceActions();
1866
+ const tables = useQuery(tablesQuery(connectionId, database));
1867
+ if (tables.isPending) return /* @__PURE__ */ jsx("div", {
1868
+ className: "x1ua62b8 x1x7g8rv xqtwvag x1tviuuy",
1869
+ children: "Loading…"
1870
+ });
1871
+ if (tables.isError) return /* @__PURE__ */ jsx("div", {
1872
+ className: "xmjl1bo xqtwvag xb0vdwp xfrs9s4 x13faqbe",
1873
+ children: errorMessage(tables.error)
1874
+ });
1875
+ const needle = filter.toLowerCase();
1876
+ const visible = tables.data.filter((t) => t.name.toLowerCase().includes(needle));
1877
+ if (visible.length === 0) return /* @__PURE__ */ jsx("div", {
1878
+ className: "x1ua62b8 x1x7g8rv xqtwvag x1tviuuy",
1879
+ children: needle ? "No matches" : "No tables"
1880
+ });
1881
+ return visible.map((t) => /* @__PURE__ */ jsxs("button", {
1882
+ type: "button",
1883
+ onClick: () => void actions.openTable(connectionId, database, t.name),
1884
+ ...{
1885
+ 0: { className: "x78zum5 x6s0dn4 x17d4w8g xh8yej3 x1jgo0hl x1xijbu4 xc342km xvjf9rd xdpxx8g x1hl2dhg xvj2dgp xo1l8bm x1ypdohk xjbqb8w x1sxlxsz x1a2a7pz xn0s4wz x1g40iwv x1x7g8rv x1tviuuy" },
1886
+ 1: { className: "x78zum5 x6s0dn4 x17d4w8g xh8yej3 x1jgo0hl x1xijbu4 xc342km xvjf9rd xdpxx8g x1hl2dhg xvj2dgp xo1l8bm x1ypdohk x1a2a7pz xn0s4wz x1g40iwv x1x7g8rv x1nke8ei x122w267" }
1887
+ }[!!(activeTable?.database === database && activeTable.table === t.name) << 0],
1888
+ children: [
1889
+ /* @__PURE__ */ jsx(Table, {}),
1890
+ /* @__PURE__ */ jsx("span", {
1891
+ className: "x98rzlu xeuugli xb3r6kr xlyipyv xuxw1ft",
1892
+ children: t.name
1893
+ }),
1894
+ t.isView ? /* @__PURE__ */ jsx("span", {
1895
+ className: "x1j6dyjg x1tviuuy",
1896
+ children: "view"
1897
+ }) : null
1898
+ ]
1899
+ }, t.name));
1900
+ }
1901
+ function Explorer({ connection, activeTable }) {
1902
+ const actions = useWorkspaceActions();
1903
+ const qc = useQueryClient();
1904
+ const { layout, update } = useLayout();
1905
+ const databases = useQuery(databasesQuery(connection.id));
1906
+ const [filterText, setFilterText] = useState("");
1907
+ const [filter] = useDebouncedValue(filterText, { wait: 250 });
1908
+ const [toggled, setToggled] = useState({});
1909
+ const filterRef = useRef(null);
1910
+ useHotkey("Mod+K", () => filterRef.current?.focus());
1911
+ const isOpen = (db) => toggled[db] ?? (db === activeTable?.database || db === connection.database);
1912
+ const needle = filter.toLowerCase();
1913
+ const visibleDbs = (databases.data ?? []).filter((db) => isOpen(db) || db.toLowerCase().includes(needle));
1914
+ return /* @__PURE__ */ jsxs("nav", {
1915
+ ...stylex.props(styles$2.explorer, styles$2.width(layout.explorerWidth)),
1916
+ "aria-label": "Explorer",
1917
+ children: [
1918
+ /* @__PURE__ */ jsxs("div", {
1919
+ className: "x78zum5 x6s0dn4 xbbb4fa x5md06r x69q509 x5ku0wl x2lah0s",
1920
+ children: [/* @__PURE__ */ jsx("span", {
1921
+ className: "x98rzlu xeuugli xvj2dgp x1s688f xb3r6kr xlyipyv xuxw1ft",
1922
+ children: connection.name
1923
+ }), /* @__PURE__ */ jsx(Tooltip$1, {
1924
+ label: "Reload databases and tables",
1925
+ children: /* @__PURE__ */ jsx(Button$1, {
1926
+ variant: "ghost",
1927
+ icon: true,
1928
+ "aria-label": "Reload",
1929
+ onClick: () => {
1930
+ qc.invalidateQueries({ queryKey: ["databases", connection.id] });
1931
+ qc.invalidateQueries({ queryKey: ["tables", connection.id] });
1932
+ qc.invalidateQueries({ queryKey: ["sqlSchema", connection.id] });
1933
+ },
1934
+ children: /* @__PURE__ */ jsx(Refresh, {})
1935
+ })
1936
+ })]
1937
+ }),
1938
+ /* @__PURE__ */ jsx("div", {
1939
+ className: "xu7byfh x1o95cbt x2lah0s",
1940
+ children: /* @__PURE__ */ jsx(Input$1, {
1941
+ inputRef: filterRef,
1942
+ placeholder: "Filter…",
1943
+ "aria-label": "Filter databases and tables",
1944
+ value: filterText,
1945
+ onChange: (e) => setFilterText(e.target.value)
1946
+ })
1947
+ }),
1948
+ /* @__PURE__ */ jsx("div", {
1949
+ className: "xu7byfh xbp0ul4 x2lah0s",
1950
+ children: /* @__PURE__ */ jsxs("button", {
1951
+ type: "button",
1952
+ onClick: () => void actions.openSql(connection.id),
1953
+ className: "x78zum5 x6s0dn4 x17d4w8g xh8yej3 x1jgo0hl x1xijbu4 xc342km xvjf9rd x122w267 xdpxx8g x1hl2dhg xvj2dgp xo1l8bm x1ypdohk xjbqb8w x1sxlxsz x1a2a7pz xn0s4wz x1g40iwv",
1954
+ children: [
1955
+ /* @__PURE__ */ jsx("span", {
1956
+ className: "x78zum5 x1tviuuy",
1957
+ children: /* @__PURE__ */ jsx(Terminal, {})
1958
+ }),
1959
+ /* @__PURE__ */ jsx("span", {
1960
+ className: "x98rzlu xeuugli xb3r6kr xlyipyv xuxw1ft",
1961
+ children: "SQL editor"
1962
+ }),
1963
+ /* @__PURE__ */ jsx(Kbd, { children: "⌥T" })
1964
+ ]
1965
+ })
1966
+ }),
1967
+ /* @__PURE__ */ jsxs("div", {
1968
+ className: "x98rzlu x1odjw0f xu7byfh xaxbvzv",
1969
+ children: [
1970
+ databases.isPending ? /* @__PURE__ */ jsx("div", {
1971
+ className: "x1ua62b8 x1x7g8rv xqtwvag x1tviuuy",
1972
+ children: "Connecting…"
1973
+ }) : null,
1974
+ databases.isError ? /* @__PURE__ */ jsx("div", {
1975
+ className: "xmjl1bo xqtwvag xb0vdwp xfrs9s4 x13faqbe",
1976
+ children: errorMessage(databases.error)
1977
+ }) : null,
1978
+ visibleDbs.map((db) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("button", {
1979
+ type: "button",
1980
+ "aria-expanded": isOpen(db),
1981
+ className: "x78zum5 x6s0dn4 x17d4w8g xh8yej3 x1jgo0hl x1xijbu4 xc342km xvjf9rd x122w267 xdpxx8g x1hl2dhg xvj2dgp x1ypdohk xjbqb8w x1sxlxsz x1a2a7pz xn0s4wz x1g40iwv xk50ysn",
1982
+ onClick: () => setToggled({
1983
+ ...toggled,
1984
+ [db]: !isOpen(db)
1985
+ }),
1986
+ children: [
1987
+ /* @__PURE__ */ jsx("span", {
1988
+ ...{
1989
+ 0: { className: "x78zum5 x1tviuuy x7p49u4 x11xpdln x19jr8i9 x6setmy" },
1990
+ 1: { className: "x78zum5 x1tviuuy x11xpdln x19jr8i9 x6setmy x1iffjtl" }
1991
+ }[!!isOpen(db) << 0],
1992
+ children: /* @__PURE__ */ jsx(ChevronRight, {})
1993
+ }),
1994
+ /* @__PURE__ */ jsx("span", {
1995
+ className: "x78zum5 x1tviuuy",
1996
+ children: /* @__PURE__ */ jsx(Database, {})
1997
+ }),
1998
+ /* @__PURE__ */ jsx("span", {
1999
+ className: "x98rzlu xeuugli xb3r6kr xlyipyv xuxw1ft",
2000
+ children: db
2001
+ })
2002
+ ]
2003
+ }), isOpen(db) ? /* @__PURE__ */ jsx(Tables, {
2004
+ connectionId: connection.id,
2005
+ database: db,
2006
+ filter,
2007
+ activeTable
2008
+ }) : null] }, db))
2009
+ ]
2010
+ }),
2011
+ /* @__PURE__ */ jsx(ResizeHandle, {
2012
+ label: "Resize explorer",
2013
+ value: layout.explorerWidth,
2014
+ min: 180,
2015
+ max: 480,
2016
+ defaultValue: 248,
2017
+ onChange: (w, commit) => update({ explorerWidth: w }, commit)
2018
+ })
2019
+ ]
2020
+ });
2021
+ }
2022
+ // src/lib/numeric.ts
2023
+ var WRAPPER = /^(?:nullable|lowcardinality)\((.*)\)$/i;
2024
+ var NUMERIC = /^(?:u?int(?:8|16|32|64|128|256)?|tinyint|smallint|mediumint|bigint|integer|decimal(?:32|64|128|256)?|dec|numeric|fixed|float(?:32|64)?|double(?: precision)?|real|bit|year|long|longlong|newdecimal|tiny|short|int24)(?:\([^)]*\))?(?:\s+(?:unsigned|signed|zerofill))*$/i;
2025
+ /** Covers MySQL column types, mysql2 result-field names and ClickHouse types, wrappers included. */
2026
+ function isNumericType(type) {
2027
+ let t = type.trim();
2028
+ for (let m = WRAPPER.exec(t); m; m = WRAPPER.exec(t)) t = (m[1] ?? "").trim();
2029
+ return NUMERIC.test(t);
2030
+ }
2031
+ // src/components/DataGrid.tsx
2032
+ var ROW_HEIGHT = 28;
2033
+ var SELECT_WIDTH = 36;
2034
+ var MIN_WIDTH = 72;
2035
+ var MAX_WIDTH = 360;
2036
+ var SAMPLE_ROWS = 50;
2037
+ var MONO_GLYPH = 7.2;
2038
+ var _temp$1 = {
2039
+ kVAEAm: "x1n2onr6",
2040
+ "$$css": true
2041
+ };
2042
+ var _temp2 = {
2043
+ k1xSpc: "xrvj5dj",
2044
+ kVAEAm: "x7wzq59",
2045
+ k87sOh: "x13vifvy",
2046
+ kY2c9j: "xhtitgo",
2047
+ kZKoxP: "xc9qbxq",
2048
+ kWkggS: "xgwgzc4",
2049
+ kt9PQ7: "xso031l",
2050
+ kfdmCh: "x1q0q8m5",
2051
+ kL6WhQ: "x1orbg20",
2052
+ "$$css": true
2053
+ };
2054
+ var _temp3 = {
2055
+ k1xSpc: "xrvj5dj",
2056
+ kZKoxP: "x1fgtraw",
2057
+ kVAEAm: "x10l6tqk",
2058
+ k87sOh: "x13vifvy",
2059
+ kbCHJM: "xu96u03",
2060
+ k3aq6I: "xsqj5wx",
2061
+ kWkggS: "xjbqb8w x1sxlxsz",
2062
+ "$$css": true
2063
+ };
2064
+ var styles$1 = {
2065
+ inner: (width, height) => [
2066
+ _temp$1,
2067
+ {
2068
+ kzqmXN: width != null ? "x5lhr3w" : width,
2069
+ kZKoxP: height != null ? "x16ye13r" : height,
2070
+ $$css: true
2071
+ },
2072
+ {
2073
+ "--x-width": ((val) => typeof val === "number" ? val + "px" : val != null ? val : void 0)(width),
2074
+ "--x-height": ((val) => typeof val === "number" ? val + "px" : val != null ? val : void 0)(height)
2075
+ }
2076
+ ],
2077
+ headRow: (template, width) => [
2078
+ _temp2,
2079
+ {
2080
+ kumcoG: template != null ? "xqketvx" : template,
2081
+ kzqmXN: width != null ? "x5lhr3w" : width,
2082
+ $$css: true
2083
+ },
2084
+ {
2085
+ "--x-gridTemplateColumns": template != null ? template : void 0,
2086
+ "--x-width": ((val) => typeof val === "number" ? val + "px" : val != null ? val : void 0)(width)
2087
+ }
2088
+ ],
2089
+ row: (template, width, y) => [
2090
+ _temp3,
2091
+ {
2092
+ kumcoG: template != null ? "xqketvx" : template,
2093
+ kzqmXN: width != null ? "x5lhr3w" : width,
2094
+ $$css: true
2095
+ },
2096
+ {
2097
+ "--x-gridTemplateColumns": template != null ? template : void 0,
2098
+ "--x-width": ((val) => typeof val === "number" ? val + "px" : val != null ? val : void 0)(width),
2099
+ "--x-transform": `translateY(${y}px)` != null ? `translateY(${y}px)` : void 0
2100
+ }
2101
+ ],
2102
+ rowSelected: {
2103
+ kWkggS: "x1vbvkw5",
2104
+ $$css: true
2105
+ }
2106
+ };
2107
+ var features = tableFeatures({
2108
+ columnSizingFeature,
2109
+ columnResizingFeature
2110
+ });
2111
+ var helper = createColumnHelper();
2112
+ var isNumeric = (c) => isNumericType(c.type);
2113
+ /** Widths from the header, the type label and the first rows, so nothing is clipped on load. */
2114
+ function measure(columns, rows) {
2115
+ const sample = rows.slice(0, SAMPLE_ROWS);
2116
+ return columns.map((c, i) => {
2117
+ const head = c.name.length * 7.4 + 20 + (c.isKey ? 18 : 0) + 18;
2118
+ const type = c.type.length * 7.2 + 20;
2119
+ const body = sample.reduce((max, row) => Math.max(max, Math.min(formatCell(row[i]).length, 60)), 0) * MONO_GLYPH + 20;
2120
+ return Math.round(Math.min(MAX_WIDTH, Math.max(MIN_WIDTH, head, type, body)));
2121
+ });
2122
+ }
2123
+ /** Remounts when the column set changes, so measured and user-set widths never leak across results. */
2124
+ function DataGrid(props) {
2125
+ return /* @__PURE__ */ jsx(Grid, { ...props }, props.columns.map((c) => c.name).join("\0"));
2126
+ }
2127
+ function Grid({ columns, rows, loading, sort, onSortChange, onCommit, selected, onSelectedChange }) {
2128
+ const scrollRef = useRef(null);
2129
+ const [editing, setEditing] = useState(null);
2130
+ const [draft, setDraft] = useState("");
2131
+ const [measured, setMeasured] = useState(null);
2132
+ if (measured === null && rows.length > 0) setMeasured(measure(columns, rows));
2133
+ const sizes = measured ?? measure(columns, []);
2134
+ const tableColumns = helper.columns(columns.map((c, i) => helper.accessor((row) => row[i], {
2135
+ id: String(i),
2136
+ header: c.name,
2137
+ size: sizes[i],
2138
+ minSize: 48,
2139
+ maxSize: 1200
2140
+ })));
2141
+ const table = useTable({
2142
+ features,
2143
+ columns: tableColumns,
2144
+ data: rows,
2145
+ columnResizeMode: "onChange"
2146
+ });
2147
+ const tableRows = table.getRowModel().rows;
2148
+ const headers = table.getHeaderGroups()[0]?.headers ?? [];
2149
+ const virtualizer = useVirtualizer({
2150
+ count: tableRows.length,
2151
+ getScrollElement: () => scrollRef.current,
2152
+ estimateSize: () => ROW_HEIGHT,
2153
+ overscan: 12
2154
+ });
2155
+ useHotkey("Escape", () => setEditing(null), { enabled: editing !== null });
2156
+ const selectable = selected !== void 0 && onSelectedChange !== void 0;
2157
+ const columnWidths = headers.map((h) => h.getSize());
2158
+ const template = `${selectable ? `${SELECT_WIDTH}px ` : ""}${columnWidths.map((w) => `${w}px`).join(" ")}`;
2159
+ const width = (selectable ? SELECT_WIDTH : 0) + columnWidths.reduce((a, b) => a + b, 0);
2160
+ const commit = async () => {
2161
+ if (!editing || !onCommit) return;
2162
+ const column = columns[editing.col];
2163
+ if (!column) return;
2164
+ const { row, original } = editing;
2165
+ setEditing(null);
2166
+ if (draft === original) return;
2167
+ await onCommit(row, column, draft === "" && column.nullable ? null : draft);
2168
+ };
2169
+ const allSelected = rows.length > 0 && selected?.size === rows.length;
2170
+ return /* @__PURE__ */ jsxs("div", {
2171
+ ref: scrollRef,
2172
+ className: "x98rzlu x2lwn1j xysyzu8 x1n2onr6 x185or1n",
2173
+ children: [
2174
+ /* @__PURE__ */ jsxs("div", {
2175
+ ...stylex.props(styles$1.headRow(template, width)),
2176
+ role: "row",
2177
+ children: [selectable ? /* @__PURE__ */ jsx("div", {
2178
+ className: "x78zum5 x6s0dn4 xl56j7k",
2179
+ children: /* @__PURE__ */ jsx(Checkbox$1, {
2180
+ ariaLabel: "Select all rows",
2181
+ checked: allSelected,
2182
+ onCheckedChange: (on) => onSelectedChange(on ? new Set(rows.map((_, i) => i)) : /* @__PURE__ */ new Set())
2183
+ })
2184
+ }) : null, headers.map((header, i) => {
2185
+ const c = columns[i];
2186
+ if (!c) return null;
2187
+ const sorted = sort?.column === c.name ? sort.direction : null;
2188
+ return /* @__PURE__ */ jsxs("div", {
2189
+ role: "columnheader",
2190
+ tabIndex: onSortChange ? 0 : void 0,
2191
+ "aria-sort": sorted === "asc" ? "ascending" : sorted === "desc" ? "descending" : "none",
2192
+ ...{
2193
+ 0: { className: "x1n2onr6 x78zum5 xdt5ytf xl56j7k x1awh872 xeuugli x1ypdohk x87ps6o x1a2a7pz xjbqb8w x1sxlxsz x1jrw5c4 x15406qy x1qfwjxi x6setmy" },
2194
+ 1: { className: "x1n2onr6 x78zum5 xdt5ytf xl56j7k x1awh872 xeuugli x1ypdohk x87ps6o x1a2a7pz xjbqb8w x1sxlxsz x1jrw5c4 x15406qy x1qfwjxi x6setmy xuk3077" }
2195
+ }[!!isNumeric(c) << 0],
2196
+ onClick: () => onSortChange?.(c.name),
2197
+ onKeyDown: (e) => {
2198
+ if (e.key !== "Enter" && e.key !== " ") return;
2199
+ e.preventDefault();
2200
+ onSortChange?.(c.name);
2201
+ },
2202
+ children: [
2203
+ /* @__PURE__ */ jsxs("span", {
2204
+ className: "x78zum5 x6s0dn4 x1jnr06f x193iq5w xqtwvag x1s688f",
2205
+ children: [
2206
+ c.isKey ? /* @__PURE__ */ jsx("span", {
2207
+ className: "x78zum5 x2lah0s x1tviuuy",
2208
+ role: "img",
2209
+ "aria-label": "Key column",
2210
+ children: /* @__PURE__ */ jsx(Key, {})
2211
+ }) : null,
2212
+ /* @__PURE__ */ jsx("span", {
2213
+ className: "xb3r6kr xlyipyv xuxw1ft",
2214
+ children: c.name
2215
+ }),
2216
+ sorted ? /* @__PURE__ */ jsx("span", {
2217
+ className: "x78zum5 x2lah0s x1tviuuy",
2218
+ children: sorted === "asc" ? /* @__PURE__ */ jsx(ArrowUp, {}) : /* @__PURE__ */ jsx(ArrowDown, {})
2219
+ }) : null
2220
+ ]
2221
+ }),
2222
+ /* @__PURE__ */ jsx("span", {
2223
+ className: "x193iq5w x1j6dyjg x1tviuuy xb3r6kr xlyipyv xuxw1ft",
2224
+ children: c.type
2225
+ }),
2226
+ /* @__PURE__ */ jsx("div", {
2227
+ role: "separator",
2228
+ "aria-orientation": "vertical",
2229
+ "aria-label": `Resize ${c.name}`,
2230
+ className: "x10l6tqk x13vifvy xzkxjte x1ey2m1c x1wc42o8 xzkaem6 xicojor x5ve5x3 x78zum5 xl56j7k",
2231
+ onMouseDown: header.getResizeHandler(),
2232
+ onTouchStart: header.getResizeHandler(),
2233
+ onClick: (e) => e.stopPropagation(),
2234
+ onKeyDown: (e) => e.stopPropagation(),
2235
+ onDoubleClick: (e) => {
2236
+ e.stopPropagation();
2237
+ header.column.resetSize();
2238
+ },
2239
+ children: /* @__PURE__ */ jsx("div", { ...{
2240
+ 0: { className: "x1i1rx1s x5yr21d x14i5uus x5o664w" },
2241
+ 1: { className: "x5yr21d x152q98y xfo62xy" }
2242
+ }[!!header.column.getIsResizing() << 0] })
2243
+ })
2244
+ ]
2245
+ }, header.id);
2246
+ })]
2247
+ }),
2248
+ rows.length === 0 ? loading ? /* @__PURE__ */ jsx(PendingPane, {}) : /* @__PURE__ */ jsx("div", {
2249
+ className: "xggk2y7 x1tviuuy xqtwvag",
2250
+ children: "No rows."
2251
+ }) : null,
2252
+ /* @__PURE__ */ jsx("div", {
2253
+ ...stylex.props(styles$1.inner(width, virtualizer.getTotalSize())),
2254
+ children: virtualizer.getVirtualItems().map((item) => {
2255
+ const row = tableRows[item.index];
2256
+ if (!row) return null;
2257
+ const isSelected = selected?.has(item.index) ?? false;
2258
+ return /* @__PURE__ */ jsxs("div", {
2259
+ role: "row",
2260
+ "data-row": item.index,
2261
+ ...stylex.props(styles$1.row(template, width, item.start), isSelected && styles$1.rowSelected),
2262
+ children: [selectable ? /* @__PURE__ */ jsx("div", {
2263
+ className: "x78zum5 x6s0dn4 xeuugli xxb57xf xqtwvag xss6m8b xb3r6kr xlyipyv xuxw1ft xso031l x1q0q8m5 x1atvb72 xl56j7k xnjsko4",
2264
+ children: /* @__PURE__ */ jsx(Checkbox$1, {
2265
+ ariaLabel: `Select row ${item.index + 1}`,
2266
+ checked: isSelected,
2267
+ onCheckedChange: (on) => {
2268
+ const next = new Set(selected);
2269
+ if (on) next.add(item.index);
2270
+ else next.delete(item.index);
2271
+ onSelectedChange(next);
2272
+ }
2273
+ })
2274
+ }) : null, row.getAllCells().map((cell, colIndex) => {
2275
+ const value = cell.getValue();
2276
+ const column = columns[colIndex];
2277
+ const isEditing = editing?.row === item.index && editing.col === colIndex;
2278
+ return /* @__PURE__ */ jsx("div", {
2279
+ role: "gridcell",
2280
+ ...{
2281
+ 0: { className: "x78zum5 x6s0dn4 x1awh872 xeuugli xxb57xf xqtwvag xss6m8b xb3r6kr xlyipyv xuxw1ft xso031l x1q0q8m5 x1atvb72" },
2282
+ 8: { className: "x78zum5 x6s0dn4 x1awh872 xeuugli xxb57xf xqtwvag xss6m8b xb3r6kr xlyipyv xuxw1ft xso031l x1q0q8m5 x1atvb72 x13a6bvl" },
2283
+ 4: { className: "x78zum5 x6s0dn4 x1awh872 xeuugli xxb57xf xqtwvag xss6m8b xb3r6kr xlyipyv xuxw1ft xso031l x1q0q8m5 x1atvb72 x18z6s4b" },
2284
+ 12: { className: "x78zum5 x6s0dn4 x1awh872 xeuugli xxb57xf xqtwvag xss6m8b xb3r6kr xlyipyv xuxw1ft xso031l x1q0q8m5 x1atvb72 x13a6bvl x18z6s4b" },
2285
+ 2: { className: "x78zum5 x6s0dn4 x1awh872 xeuugli xxb57xf xqtwvag xss6m8b xb3r6kr xlyipyv xuxw1ft xso031l x1q0q8m5 x1atvb72 x1tviuuy x1k4tb9n" },
2286
+ 10: { className: "x78zum5 x6s0dn4 x1awh872 xeuugli xxb57xf xqtwvag xss6m8b xb3r6kr xlyipyv xuxw1ft xso031l x1q0q8m5 x1atvb72 x13a6bvl x1tviuuy x1k4tb9n" },
2287
+ 6: { className: "x78zum5 x6s0dn4 x1awh872 xeuugli xxb57xf xqtwvag xss6m8b xb3r6kr xlyipyv xuxw1ft xso031l x1q0q8m5 x1atvb72 x18z6s4b x1tviuuy x1k4tb9n" },
2288
+ 14: { className: "x78zum5 x6s0dn4 x1awh872 xeuugli xxb57xf xqtwvag xss6m8b xb3r6kr xlyipyv xuxw1ft xso031l x1q0q8m5 x1atvb72 x13a6bvl x18z6s4b x1tviuuy x1k4tb9n" },
2289
+ 1: { className: "x78zum5 x6s0dn4 x1awh872 xeuugli xxb57xf xqtwvag xss6m8b xlyipyv xuxw1ft xso031l x1q0q8m5 x1atvb72 x1717udv x1rea2x4" },
2290
+ 9: { className: "x78zum5 x6s0dn4 x1awh872 xeuugli xxb57xf xqtwvag xss6m8b xlyipyv xuxw1ft xso031l x1q0q8m5 x1atvb72 x13a6bvl x1717udv x1rea2x4" },
2291
+ 5: { className: "x78zum5 x6s0dn4 x1awh872 xeuugli xxb57xf xqtwvag xss6m8b xlyipyv xuxw1ft xso031l x1q0q8m5 x1atvb72 x18z6s4b x1717udv x1rea2x4" },
2292
+ 13: { className: "x78zum5 x6s0dn4 x1awh872 xeuugli xxb57xf xqtwvag xss6m8b xlyipyv xuxw1ft xso031l x1q0q8m5 x1atvb72 x13a6bvl x18z6s4b x1717udv x1rea2x4" },
2293
+ 3: { className: "x78zum5 x6s0dn4 x1awh872 xeuugli xxb57xf xqtwvag xss6m8b xlyipyv xuxw1ft xso031l x1q0q8m5 x1atvb72 x1tviuuy x1k4tb9n x1717udv x1rea2x4" },
2294
+ 11: { className: "x78zum5 x6s0dn4 x1awh872 xeuugli xxb57xf xqtwvag xss6m8b xlyipyv xuxw1ft xso031l x1q0q8m5 x1atvb72 x13a6bvl x1tviuuy x1k4tb9n x1717udv x1rea2x4" },
2295
+ 7: { className: "x78zum5 x6s0dn4 x1awh872 xeuugli xxb57xf xqtwvag xss6m8b xlyipyv xuxw1ft xso031l x1q0q8m5 x1atvb72 x18z6s4b x1tviuuy x1k4tb9n x1717udv x1rea2x4" },
2296
+ 15: { className: "x78zum5 x6s0dn4 x1awh872 xeuugli xxb57xf xqtwvag xss6m8b xlyipyv xuxw1ft xso031l x1q0q8m5 x1atvb72 x13a6bvl x18z6s4b x1tviuuy x1k4tb9n x1717udv x1rea2x4" }
2297
+ }[!!(column && isNumeric(column)) << 3 | !!onCommit << 2 | !!(value === null) << 1 | !!isEditing << 0],
2298
+ title: formatCell(value),
2299
+ onDoubleClick: () => {
2300
+ if (!onCommit) return;
2301
+ const text = value === null || value === void 0 ? "" : formatCell(value);
2302
+ setDraft(text);
2303
+ setEditing({
2304
+ row: item.index,
2305
+ col: colIndex,
2306
+ original: text
2307
+ });
2308
+ },
2309
+ children: isEditing ? /* @__PURE__ */ jsx(Input$1, {
2310
+ autoFocus: true,
2311
+ "aria-label": `Edit ${columns[colIndex]?.name ?? "cell"}`,
2312
+ value: draft,
2313
+ onChange: (e) => setDraft(e.target.value),
2314
+ onFocus: (e) => e.currentTarget.select(),
2315
+ onBlur: () => setEditing(null),
2316
+ onKeyDown: (e) => {
2317
+ if (e.key === "Enter") {
2318
+ e.preventDefault();
2319
+ commit();
2320
+ }
2321
+ }
2322
+ }) : formatCell(value)
2323
+ }, cell.id);
2324
+ })]
2325
+ }, row.id);
2326
+ })
2327
+ })
2328
+ ]
2329
+ });
2330
+ }
2331
+ // src/lib/sqlDialects.ts
2332
+ var words = (list) => list.split(/\s+/).filter(Boolean);
2333
+ var CLICKHOUSE_KEYWORDS = words(`
2334
+ add after alias all alter and anti any array as asc ascending asof assume attach between by
2335
+ case cast check cluster codec collate column columns comment cross cube database databases
2336
+ default delete desc descending describe detach dictionary distinct distributed drop else
2337
+ end engine except exchange exists explain extract fetch fill final first for format from
2338
+ full global grant group having ilike in index inner insert interval into is join key kill
2339
+ last left like limit live materialized modify move mutation natural not null nulls offset on
2340
+ optimize option or order outer outfile over partition populate prewhere primary projection
2341
+ qualify query refresh rename replace right rollup sample select semi set settings show
2342
+ step system table tables temporary then to top totals truncate ttl union unique update use
2343
+ using values view volume watch when where window with
2344
+ `);
2345
+ var CLICKHOUSE_TYPES = words(`
2346
+ AggregateFunction Array Bool Date Date32 DateTime DateTime64 Decimal Decimal32 Decimal64
2347
+ Decimal128 Decimal256 Dynamic Enum Enum8 Enum16 FixedString Float32 Float64 IPv4 IPv6 Int8
2348
+ Int16 Int32 Int64 Int128 Int256 JSON LowCardinality Map Nested Nullable Point Polygon Ring
2349
+ SimpleAggregateFunction String Tuple UInt8 UInt16 UInt32 UInt64 UInt128 UInt256 UUID Variant
2350
+ `);
2351
+ var CLICKHOUSE_FUNCTIONS = words(`
2352
+ abs acos any anyHeavy anyLast argMax argMin array arrayAll arrayAvg arrayConcat arrayCount
2353
+ arrayCumSum arrayDistinct arrayElement arrayEnumerate arrayExists arrayFill arrayFilter
2354
+ arrayFirst arrayFlatten arrayIntersect arrayJoin arrayMap arrayMax arrayMin arrayPopBack
2355
+ arrayPopFront arrayProduct arrayPushBack arrayPushFront arrayReduce arrayResize arrayReverse
2356
+ arraySlice arraySort arraySplit arraySum arrayUniq arrayZip asin atan avg avgIf avgWeighted
2357
+ base64Decode base64Encode bitAnd bitCount bitOr bitShiftLeft bitShiftRight bitXor bar
2358
+ ceil coalesce concat concatWithSeparator corr cos count countDistinct countIf countSubstrings
2359
+ covarPop covarSamp cutQueryString cutURLParameter dateDiff dateName dateTrunc date_trunc
2360
+ decodeURLComponent dictGet dictGetOrDefault dictHas domain domainWithoutWWW dumpColumnStructure
2361
+ empty emptyArrayString encodeURLComponent endsWith exp extract extractAll extractGroups
2362
+ extractURLParameter extractURLParameters firstSignificantSubdomain floor formatDateTime
2363
+ formatReadableQuantity formatReadableSize formatReadableTimeDelta fromUnixTimestamp
2364
+ fromUnixTimestamp64Milli generateUUIDv4 greatest groupArray groupArrayInsertAt groupArrayMovingAvg
2365
+ groupArraySample groupBitAnd groupBitOr groupBitXor groupUniqArray has hasAll hasAny hasToken
2366
+ hex hostName if ifNull indexOf intDiv intDivOrZero isFinite isInfinite isNaN isNotNull isNull
2367
+ isValidUTF8 JSONExtract JSONExtractArrayRaw JSONExtractBool JSONExtractFloat JSONExtractInt
2368
+ JSONExtractKeys JSONExtractKeysAndValues JSONExtractRaw JSONExtractString JSONExtractUInt
2369
+ JSONHas JSONLength JSONType JSON_VALUE JSON_QUERY kurtPop kurtSamp lagInFrame
2370
+ last_value leadInFrame least left leftPad length lengthUTF8 log log10 log2 lower lowerUTF8
2371
+ map mapKeys mapValues match materialize max maxIf median min minIf modulo multiIf
2372
+ multiMatchAny murmurHash3_64 neighbor not notEmpty notLike now now64 nullIf numbers
2373
+ parseDateTime parseDateTimeBestEffort parseDateTimeBestEffortOrNull parseURL path
2374
+ position positionCaseInsensitive pow protocol quantile quantileDeterministic quantileExact
2375
+ quantileExactWeighted quantileTDigest quantileTiming quantiles quantilesExact
2376
+ quantilesTDigest quantilesTiming rand rand64 range rank regexpExtract regexpReplaceAll
2377
+ regexpReplaceOne replace replaceAll replaceOne replaceRegexpAll replaceRegexpOne reverse right
2378
+ rightPad round roundBankers row_number rowNumberInBlock runningDifference sin skewPop skewSamp
2379
+ splitByChar splitByRegexp splitByString sqrt startsWith stddevPop stddevSamp substring
2380
+ substringUTF8 sum sumIf sumMap sumWithOverflow tan today toDate toDate32 toDateOrNull
2381
+ toDateOrZero toDateTime toDateTime64 toDayOfMonth toDayOfWeek toDayOfYear toDecimal32
2382
+ toDecimal64 toDecimal128 toFloat32 toFloat64 toFloat64OrNull toHour toInt8 toInt16 toInt32
2383
+ toInt64 toInt32OrNull toInt64OrNull toIntervalDay toIntervalHour toIntervalMinute
2384
+ toIntervalMonth toIntervalSecond toIntervalWeek toIntervalYear toISOWeek toISOYear toJSONString
2385
+ toLastDayOfMonth toLowCardinality toMinute toMonday toMonth toNullable toQuarter toRelativeDayNum
2386
+ toRelativeHourNum toRelativeMonthNum toSecond toStartOfDay toStartOfFifteenMinutes
2387
+ toStartOfFiveMinutes toStartOfHour toStartOfInterval toStartOfMinute toStartOfMonth
2388
+ toStartOfQuarter toStartOfTenMinutes toStartOfWeek toStartOfYear toString toTime toTimeZone
2389
+ toTypeName toUInt8 toUInt16 toUInt32 toUInt64 toUInt32OrNull toUInt64OrNull toUnixTimestamp
2390
+ toUnixTimestamp64Milli toUUID toWeek toYYYYMM toYYYYMMDD toYYYYMMDDhhmmss toYear
2391
+ topK topKWeighted trim trimBoth trimLeft trimRight tuple tupleElement uniq uniqCombined
2392
+ uniqCombined64 uniqExact uniqHLL12 uniqTheta upper upperUTF8 uptime
2393
+ varPop varSamp version yesterday
2394
+ `);
2395
+ var CANONICAL = new Map([...CLICKHOUSE_TYPES, ...CLICKHOUSE_FUNCTIONS].map((name) => [name.toLowerCase(), name]));
2396
+ /** lang-sql looks words up lowercased, so the dialect holds lowercase and CANONICAL restores case. */
2397
+ var clickhouse = SQLDialect.define({
2398
+ keywords: CLICKHOUSE_KEYWORDS.join(" "),
2399
+ types: CLICKHOUSE_TYPES.join(" ").toLowerCase(),
2400
+ builtin: CLICKHOUSE_FUNCTIONS.join(" ").toLowerCase(),
2401
+ backslashEscapes: true,
2402
+ hashComments: true,
2403
+ identifierQuotes: "`\"",
2404
+ charSetCasts: false,
2405
+ operatorChars: "*+-%<>!=&|^/"
2406
+ });
2407
+ var dialects = {
2408
+ mysql: MySQL,
2409
+ clickhouse
2410
+ };
2411
+ /** Keywords come out upper-case; built-ins and types keep their own case and get the function icon. */
2412
+ var keywordCompletions = {
2413
+ mysql: (label, type) => ({
2414
+ label,
2415
+ type: type === "variable" ? "function" : type
2416
+ }),
2417
+ clickhouse: (label, type) => ({
2418
+ label: CANONICAL.get(label.toLowerCase()) ?? label,
2419
+ type: type === "variable" ? "function" : type
2420
+ })
2421
+ };
2422
+ var IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
2423
+ function named(label, type, detail) {
2424
+ return {
2425
+ label,
2426
+ type,
2427
+ detail,
2428
+ ...IDENTIFIER.test(label) ? {} : { apply: `\`${label.replaceAll("`", "``")}\`` }
2429
+ };
2430
+ }
2431
+ /** lang-sql splits namespace keys on `.`, so a literal dot in a name is escaped. */
2432
+ var key = (name) => name.replaceAll(".", "\\.");
2433
+ function namespace(data) {
2434
+ const out = Object.create(null);
2435
+ for (const [db, tables] of Object.entries(data?.schema ?? {})) {
2436
+ const children = Object.create(null);
2437
+ for (const [table, columns] of Object.entries(tables)) children[key(table)] = {
2438
+ self: named(table, "class", "table"),
2439
+ children: columns.map((c) => named(c.name, "property", c.type))
2440
+ };
2441
+ out[key(db)] = {
2442
+ self: named(db, "namespace", "database"),
2443
+ children
2444
+ };
2445
+ }
2446
+ return out;
2447
+ }
2448
+ function sqlLanguage(kind, data) {
2449
+ return sql({
2450
+ dialect: dialects[kind],
2451
+ schema: namespace(data),
2452
+ defaultSchema: data?.defaultDatabase ?? void 0,
2453
+ upperCaseKeywords: true,
2454
+ keywordCompletion: keywordCompletions[kind]
2455
+ });
2456
+ }
2457
+ // src/lib/sqlEditorTheme.ts
2458
+ var iconGlyph = (content, tint) => ({
2459
+ "&:after": { content: `"${content}"` },
2460
+ color: tint,
2461
+ opacity: 1
2462
+ });
2463
+ var sqlEditorTheme = [EditorView.theme({
2464
+ "&": {
2465
+ height: "100%",
2466
+ color: color.text,
2467
+ backgroundColor: color.surface,
2468
+ fontSize: fontSize.xs,
2469
+ borderRadius: radius.md,
2470
+ border: `1px solid ${color.borderStrong}`
2471
+ },
2472
+ "&.cm-focused": {
2473
+ outline: "none",
2474
+ borderColor: color.primary
2475
+ },
2476
+ ".cm-scroller": {
2477
+ fontFamily: font.mono,
2478
+ lineHeight: "1.6",
2479
+ overflow: "auto"
2480
+ },
2481
+ ".cm-content": {
2482
+ padding: "10px",
2483
+ caretColor: color.text
2484
+ },
2485
+ ".cm-line": { padding: "0" },
2486
+ ".cm-cursor, .cm-dropCursor": { borderLeftColor: color.text },
2487
+ ".cm-placeholder": { color: color.faint },
2488
+ "&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": { backgroundColor: color.selection },
2489
+ ".cm-selectionMatch": { backgroundColor: color.hover },
2490
+ "&.cm-focused .cm-matchingBracket": {
2491
+ backgroundColor: color.active,
2492
+ outline: `1px solid ${color.borderStrong}`
2493
+ },
2494
+ "&.cm-focused .cm-nonmatchingBracket": { backgroundColor: color.dangerBg },
2495
+ ".cm-panels": {
2496
+ backgroundColor: color.raised,
2497
+ color: color.text
2498
+ },
2499
+ ".cm-searchMatch": { backgroundColor: color.active },
2500
+ ".cm-searchMatch.cm-searchMatch-selected": { backgroundColor: color.selection },
2501
+ ".cm-tooltip": {
2502
+ fontFamily: font.mono,
2503
+ fontSize: fontSize.xs,
2504
+ color: color.text,
2505
+ backgroundColor: color.raised,
2506
+ border: `1px solid ${color.borderStrong}`,
2507
+ borderRadius: radius.lg,
2508
+ boxShadow: `0 8px 24px ${color.shadow}`,
2509
+ overflow: "hidden"
2510
+ },
2511
+ ".cm-tooltip.cm-tooltip-autocomplete": {
2512
+ "& > ul": {
2513
+ fontFamily: font.mono,
2514
+ maxHeight: "240px",
2515
+ minWidth: "220px",
2516
+ padding: "4px"
2517
+ },
2518
+ "& > ul > li": {
2519
+ display: "flex",
2520
+ alignItems: "center",
2521
+ padding: "2px 8px 2px 4px",
2522
+ borderRadius: radius.sm,
2523
+ lineHeight: "1.6"
2524
+ },
2525
+ "& > ul > li[aria-selected]": {
2526
+ backgroundColor: color.active,
2527
+ color: color.text
2528
+ },
2529
+ "& .cm-completionLabel": { color: color.text },
2530
+ "& .cm-completionMatchedText": {
2531
+ textDecoration: "none",
2532
+ fontWeight: "700",
2533
+ color: color.primary
2534
+ },
2535
+ "& .cm-completionDetail": {
2536
+ marginLeft: "auto",
2537
+ paddingLeft: "20px",
2538
+ fontStyle: "normal",
2539
+ color: color.faint
2540
+ },
2541
+ "& .cm-completionIcon": {
2542
+ width: "1.6em",
2543
+ paddingRight: "0",
2544
+ fontSize: "100%",
2545
+ fontWeight: "600",
2546
+ textAlign: "center",
2547
+ color: color.faint,
2548
+ opacity: 1
2549
+ },
2550
+ "& .cm-completionIcon-namespace": iconGlyph("D", color.synKeyword),
2551
+ "& .cm-completionIcon-class": iconGlyph("T", color.synFunction),
2552
+ "& .cm-completionIcon-property": iconGlyph("c", color.synString),
2553
+ "& .cm-completionIcon-function": iconGlyph("ƒ", color.synNumber),
2554
+ "& .cm-completionIcon-keyword": iconGlyph("k", color.faint),
2555
+ "& .cm-completionIcon-type": iconGlyph("t", color.synType),
2556
+ "& .cm-completionIcon-constant": iconGlyph("a", color.muted)
2557
+ }
2558
+ }, { dark: true }), syntaxHighlighting(HighlightStyle.define([
2559
+ {
2560
+ tag: tags.keyword,
2561
+ color: color.synKeyword
2562
+ },
2563
+ {
2564
+ tag: tags.typeName,
2565
+ color: color.synType
2566
+ },
2567
+ {
2568
+ tag: tags.standard(tags.name),
2569
+ color: color.synFunction
2570
+ },
2571
+ {
2572
+ tag: tags.special(tags.string),
2573
+ color: color.text
2574
+ },
2575
+ {
2576
+ tag: tags.string,
2577
+ color: color.synString
2578
+ },
2579
+ {
2580
+ tag: [
2581
+ tags.number,
2582
+ tags.bool,
2583
+ tags.null
2584
+ ],
2585
+ color: color.synNumber
2586
+ },
2587
+ {
2588
+ tag: [tags.lineComment, tags.blockComment],
2589
+ color: color.synComment,
2590
+ fontStyle: "italic"
2591
+ },
2592
+ {
2593
+ tag: [
2594
+ tags.operator,
2595
+ tags.punctuation,
2596
+ tags.paren,
2597
+ tags.squareBracket
2598
+ ],
2599
+ color: color.muted
2600
+ },
2601
+ {
2602
+ tag: tags.name,
2603
+ color: color.text
2604
+ }
2605
+ ]))];
2606
+ // src/components/SqlCodeEditor.tsx
2607
+ var languageSlot = new Compartment();
2608
+ function statusOf(state) {
2609
+ return {
2610
+ empty: state.doc.length === 0,
2611
+ hasSelection: !state.selection.main.empty
2612
+ };
2613
+ }
2614
+ function SqlCodeEditor({ ref, kind, schema, initialDoc, onStatus, onDocChange, onRun }) {
2615
+ const host = useRef(null);
2616
+ const view = useRef(null);
2617
+ const parked = useRef(null);
2618
+ const initial = useRef(initialDoc);
2619
+ const latest = useRef({
2620
+ onStatus,
2621
+ onDocChange,
2622
+ onRun
2623
+ });
2624
+ useEffect(() => {
2625
+ latest.current = {
2626
+ onStatus,
2627
+ onDocChange,
2628
+ onRun
2629
+ };
2630
+ });
2631
+ useEffect(() => {
2632
+ const parent = host.current;
2633
+ if (!parent) return;
2634
+ const restored = parked.current;
2635
+ const editor = new EditorView({
2636
+ parent,
2637
+ state: restored?.state ?? EditorState.create({
2638
+ doc: initial.current,
2639
+ extensions: [
2640
+ Prec.highest(keymap.of([{
2641
+ key: "Mod-Enter",
2642
+ run: () => {
2643
+ latest.current.onRun();
2644
+ return true;
2645
+ }
2646
+ }])),
2647
+ languageSlot.of([]),
2648
+ autocompletion({
2649
+ icons: true,
2650
+ maxRenderedOptions: 80
2651
+ }),
2652
+ history(),
2653
+ drawSelection(),
2654
+ indentOnInput(),
2655
+ bracketMatching(),
2656
+ closeBrackets(),
2657
+ highlightSelectionMatches(),
2658
+ keymap.of([
2659
+ ...closeBracketsKeymap,
2660
+ ...defaultKeymap,
2661
+ ...searchKeymap,
2662
+ ...historyKeymap
2663
+ ]),
2664
+ EditorView.lineWrapping,
2665
+ EditorView.contentAttributes.of({
2666
+ "aria-label": "SQL",
2667
+ spellcheck: "false",
2668
+ autocapitalize: "off",
2669
+ autocorrect: "off"
2670
+ }),
2671
+ placeholder("SELECT * FROM …"),
2672
+ sqlEditorTheme,
2673
+ EditorView.updateListener.of((update) => {
2674
+ if (update.docChanged) latest.current.onDocChange(update.state.doc.toString());
2675
+ if (update.docChanged || update.selectionSet) latest.current.onStatus(statusOf(update.state));
2676
+ })
2677
+ ]
2678
+ })
2679
+ });
2680
+ view.current = editor;
2681
+ if (restored) {
2682
+ editor.scrollDOM.scrollTop = restored.scrollTop;
2683
+ if (restored.focused) editor.focus();
2684
+ }
2685
+ return () => {
2686
+ parked.current = {
2687
+ state: editor.state,
2688
+ scrollTop: editor.scrollDOM.scrollTop,
2689
+ focused: editor.hasFocus
2690
+ };
2691
+ editor.destroy();
2692
+ view.current = null;
2693
+ };
2694
+ }, []);
2695
+ useEffect(() => {
2696
+ view.current?.dispatch({ effects: languageSlot.reconfigure(sqlLanguage(kind, schema)) });
2697
+ }, [kind, schema]);
2698
+ useImperativeHandle(ref, () => ({ runnableText: () => {
2699
+ const state = view.current?.state;
2700
+ if (!state) return "";
2701
+ const { from, to, empty } = state.selection.main;
2702
+ return empty ? state.doc.toString() : state.sliceDoc(from, to);
2703
+ } }));
2704
+ return /* @__PURE__ */ jsx("div", {
2705
+ ref: host,
2706
+ className: "x5yr21d"
2707
+ });
2708
+ }
2709
+ // src/components/SqlEditor.tsx
2710
+ function Summary({ result }) {
2711
+ const elapsed = /* @__PURE__ */ jsxs(Fragment, { children: [
2712
+ " · ",
2713
+ /* @__PURE__ */ jsx(Num, { children: result.elapsedMs }),
2714
+ " ms"
2715
+ ] });
2716
+ if (result.columns.length > 0) return /* @__PURE__ */ jsxs(Fragment, { children: [
2717
+ /* @__PURE__ */ jsx(Num, { children: result.rowCount }),
2718
+ " row",
2719
+ result.rowCount === 1 ? "" : "s",
2720
+ result.truncated ? /* @__PURE__ */ jsxs(Fragment, { children: [
2721
+ " ",
2722
+ "(truncated at ",
2723
+ /* @__PURE__ */ jsx(Num, { children: "1000" }),
2724
+ ")"
2725
+ ] }) : null,
2726
+ elapsed
2727
+ ] });
2728
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2729
+ "OK",
2730
+ result.affectedRows !== void 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
2731
+ " · ",
2732
+ /* @__PURE__ */ jsx(Num, { children: result.affectedRows }),
2733
+ " rows affected"
2734
+ ] }) : null,
2735
+ elapsed
2736
+ ] });
2737
+ }
2738
+ var DRAFT_SAVE_MS = 500;
2739
+ var NO_DATABASE = "";
2740
+ function SqlEditor({ tab }) {
2741
+ const connection = useQuery(connectionsQuery()).data?.find((c) => c.id === tab.target.connectionId);
2742
+ return connection ? /* @__PURE__ */ jsx(SqlTab, {
2743
+ tab,
2744
+ connection
2745
+ }) : null;
2746
+ }
2747
+ function SqlTab({ tab, connection }) {
2748
+ const connectionId = connection.id;
2749
+ const { database } = tab.target;
2750
+ const qc = useQueryClient();
2751
+ const actions = useWorkspaceActions();
2752
+ const schema = useQuery(sqlSchemaQuery(connectionId));
2753
+ const databases = useQuery(databasesQuery(connectionId));
2754
+ const completion = schema.data && {
2755
+ ...schema.data,
2756
+ defaultDatabase: database ?? schema.data.defaultDatabase
2757
+ };
2758
+ const current = database ?? connection.database ?? NO_DATABASE;
2759
+ const databaseOptions = [.../* @__PURE__ */ new Set([...current === NO_DATABASE ? [] : [current], ...databases.data ?? []])].map((d) => ({
2760
+ value: d,
2761
+ label: d
2762
+ }));
2763
+ if (current === NO_DATABASE) databaseOptions.unshift({
2764
+ value: NO_DATABASE,
2765
+ label: "No database"
2766
+ });
2767
+ const saveDraft = useDebouncer((sql) => void actions.saveDraft(tab.id, sql), {
2768
+ wait: DRAFT_SAVE_MS,
2769
+ onUnmount: (debouncer) => debouncer.flush()
2770
+ });
2771
+ useEffect(() => {
2772
+ const flush = () => saveDraft.flush();
2773
+ document.addEventListener("visibilitychange", flush);
2774
+ window.addEventListener("pagehide", flush);
2775
+ return () => {
2776
+ document.removeEventListener("visibilitychange", flush);
2777
+ window.removeEventListener("pagehide", flush);
2778
+ };
2779
+ }, [saveDraft]);
2780
+ const [status, setStatus] = useState({
2781
+ empty: tab.sql.length === 0,
2782
+ hasSelection: false
2783
+ });
2784
+ const [outcome, setOutcome] = useState(null);
2785
+ const editor = useRef(null);
2786
+ const run = useMutation({
2787
+ mutationFn: runQuery,
2788
+ onSuccess: async (res) => {
2789
+ setOutcome(res);
2790
+ if (res.ok && res.result.columns.length === 0) {
2791
+ await qc.invalidateQueries({ queryKey: ["tables", connectionId] });
2792
+ await qc.invalidateQueries({ queryKey: ["rows", connectionId] });
2793
+ await qc.invalidateQueries({ queryKey: ["schema", connectionId] });
2794
+ await qc.invalidateQueries({ queryKey: ["sqlSchema", connectionId] });
2795
+ }
2796
+ },
2797
+ onError: (error) => setOutcome({
2798
+ ok: false,
2799
+ error: errorMessage(error)
2800
+ })
2801
+ });
2802
+ const execute = () => {
2803
+ const text = editor.current?.runnableText().trim() ?? "";
2804
+ if (text && !run.isPending) run.mutate({ data: {
2805
+ connectionId,
2806
+ database,
2807
+ sql: text
2808
+ } });
2809
+ };
2810
+ useHotkey("Mod+Enter", execute, { ignoreDefaultPrevented: true });
2811
+ const result = outcome?.ok ? outcome.result : null;
2812
+ return /* @__PURE__ */ jsxs("div", {
2813
+ className: "x78zum5 xdt5ytf x98rzlu x2lwn1j",
2814
+ children: [
2815
+ /* @__PURE__ */ jsxs("div", {
2816
+ className: "x78zum5 x6s0dn4 x1qughib x5md06r xgt6uh4 x2lah0s",
2817
+ children: [/* @__PURE__ */ jsx("div", {
2818
+ className: "x16grhtn",
2819
+ children: /* @__PURE__ */ jsx(Select$1, {
2820
+ ariaLabel: "Database",
2821
+ value: current,
2822
+ options: databaseOptions,
2823
+ onValueChange: (next) => void actions.setDatabase(tab.id, next || null)
2824
+ })
2825
+ }), /* @__PURE__ */ jsxs("div", {
2826
+ className: "x78zum5 x6s0dn4 xbbb4fa",
2827
+ children: [
2828
+ /* @__PURE__ */ jsx(Tooltip$1, {
2829
+ label: "Refresh schema for autocomplete",
2830
+ children: /* @__PURE__ */ jsx(Button$1, {
2831
+ variant: "ghost",
2832
+ icon: true,
2833
+ "aria-label": "Refresh schema",
2834
+ disabled: schema.isFetching,
2835
+ onClick: () => void schema.refetch(),
2836
+ children: /* @__PURE__ */ jsx(Refresh, {})
2837
+ })
2838
+ }),
2839
+ /* @__PURE__ */ jsx(Kbd, { children: "⌘↵" }),
2840
+ /* @__PURE__ */ jsxs(Button$1, {
2841
+ variant: "primary",
2842
+ onClick: execute,
2843
+ disabled: run.isPending || status.empty,
2844
+ children: [
2845
+ /* @__PURE__ */ jsx(Play, {}),
2846
+ " ",
2847
+ run.isPending ? "Running…" : status.hasSelection ? "Run selection" : "Run"
2848
+ ]
2849
+ })
2850
+ ]
2851
+ })]
2852
+ }),
2853
+ /* @__PURE__ */ jsx("div", {
2854
+ className: "xjr6psv xaxbvzv x2lah0s xrskzf1",
2855
+ children: /* @__PURE__ */ jsx(SqlCodeEditor, {
2856
+ ref: editor,
2857
+ kind: connection.kind,
2858
+ schema: completion,
2859
+ initialDoc: tab.sql,
2860
+ onDocChange: saveDraft.maybeExecute,
2861
+ onStatus: setStatus,
2862
+ onRun: execute
2863
+ })
2864
+ }),
2865
+ /* @__PURE__ */ jsxs("div", {
2866
+ className: "x78zum5 x6s0dn4 x1qughib x10w6t97 xgt6uh4 x2lah0s xqtwvag x1tviuuy xgwgzc4 x178xt8z x13fuv20 x10gwal6 xso031l x1q0q8m5 x1atvb72",
2867
+ children: [/* @__PURE__ */ jsx("span", {
2868
+ className: "xk50ysn x122w267",
2869
+ children: "Results"
2870
+ }), /* @__PURE__ */ jsx("span", { children: result ? /* @__PURE__ */ jsx(Summary, { result }) : null })]
2871
+ }),
2872
+ /* @__PURE__ */ jsxs("div", {
2873
+ className: "x78zum5 xdt5ytf x98rzlu x2lwn1j",
2874
+ children: [
2875
+ outcome && !outcome.ok ? /* @__PURE__ */ jsx("div", {
2876
+ role: "alert",
2877
+ className: "xwdmyjr xmjl1bo x1b02387 xqtwvag xb0vdwp x1imhpfg xmkeg23 x1y0btm7 xb1jnos xvjf9rd x126k92a x13faqbe",
2878
+ children: outcome.error
2879
+ }) : null,
2880
+ result && result.columns.length > 0 ? /* @__PURE__ */ jsx(DataGrid, {
2881
+ columns: result.columns,
2882
+ rows: result.rows
2883
+ }) : null,
2884
+ !outcome ? /* @__PURE__ */ jsx("div", {
2885
+ className: "x1a2dy25 xqtwvag x1tviuuy",
2886
+ children: "Run a query to see results."
2887
+ }) : null
2888
+ ]
2889
+ })
2890
+ ]
2891
+ });
2892
+ }
2893
+ // src/components/InsertRowDialog.tsx
2894
+ function InsertRowDialog({ columns, onInsert, onClose }) {
2895
+ const [values, setValues] = useState({});
2896
+ const [error, setError] = useState(null);
2897
+ const [submitting, setSubmitting] = useState(false);
2898
+ const submit = async () => {
2899
+ if (submitting) return;
2900
+ const filled = Object.fromEntries(Object.entries(values).filter(([, v]) => v !== ""));
2901
+ setSubmitting(true);
2902
+ setError(null);
2903
+ try {
2904
+ await onInsert(filled);
2905
+ onClose();
2906
+ } catch (e) {
2907
+ setError(errorMessage(e));
2908
+ setSubmitting(false);
2909
+ }
2910
+ };
2911
+ return /* @__PURE__ */ jsx(Dialog$1, {
2912
+ open: true,
2913
+ onOpenChange: (open) => !open && onClose(),
2914
+ title: "Add row",
2915
+ children: /* @__PURE__ */ jsxs("form", {
2916
+ className: "x78zum5 xdt5ytf x883omv",
2917
+ onSubmit: (e) => {
2918
+ e.preventDefault();
2919
+ submit();
2920
+ },
2921
+ children: [
2922
+ /* @__PURE__ */ jsx("p", {
2923
+ className: "x1j6dyjg x1tviuuy x1ghz6dp",
2924
+ children: "Leave a field empty to use its default (or NULL)."
2925
+ }),
2926
+ columns.map((c) => /* @__PURE__ */ jsx(Field, {
2927
+ label: `${c.name} ${c.type}${c.default ? ` default: ${c.default}` : ""}`,
2928
+ children: /* @__PURE__ */ jsx(Input$1, {
2929
+ mono: true,
2930
+ "aria-label": c.name,
2931
+ value: values[c.name] ?? "",
2932
+ onChange: (e) => setValues({
2933
+ ...values,
2934
+ [c.name]: e.target.value
2935
+ })
2936
+ })
2937
+ }, c.name)),
2938
+ error ? /* @__PURE__ */ jsx("div", {
2939
+ role: "alert",
2940
+ className: "xfifm61 xb0vdwp x13faqbe",
2941
+ children: error
2942
+ }) : null,
2943
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(Button$1, {
2944
+ type: "button",
2945
+ onClick: onClose,
2946
+ children: "Cancel"
2947
+ }), /* @__PURE__ */ jsx(Button$1, {
2948
+ type: "submit",
2949
+ variant: "primary",
2950
+ disabled: submitting,
2951
+ children: "Insert row"
2952
+ })] })
2953
+ ]
2954
+ })
2955
+ });
2956
+ }
2957
+ // src/components/ui/Tabs.tsx
2958
+ function Tabs$1({ value, onValueChange, items }) {
2959
+ return /* @__PURE__ */ jsxs(Tabs.Root, {
2960
+ value,
2961
+ onValueChange: (v) => onValueChange(v),
2962
+ className: "x78zum5 xdt5ytf x98rzlu x2lwn1j",
2963
+ children: [/* @__PURE__ */ jsx(Tabs.List, {
2964
+ className: "x78zum5 x195vfkc xdqyycr xso031l x1q0q8m5 x1atvb72 x2lah0s",
2965
+ children: items.map((t) => /* @__PURE__ */ jsx(Tabs.Tab, {
2966
+ value: t.value,
2967
+ className: "x10w6t97 x1awh872 xqtwvag xk50ysn xjbqb8w xc342km xlxy82 x1q0q8m5 x16stqrj x1m6keyf x1tviuuy x1jo8w1h xxbnkst xrf8owv x1qfwjxi x6setmy x1ypdohk x1a2a7pz xn0s4wz x2ssjo2",
2968
+ children: t.label
2969
+ }, t.value))
2970
+ }), items.map((t) => /* @__PURE__ */ jsx(Tabs.Panel, {
2971
+ value: t.value,
2972
+ className: "x78zum5 xdt5ytf x98rzlu x2lwn1j",
2973
+ children: t.content
2974
+ }, t.value))]
2975
+ });
2976
+ }
2977
+ // src/components/TableView.tsx
2978
+ var PAGE_SIZE = 200;
2979
+ var NO_SELECTION = /* @__PURE__ */ new Set();
2980
+ function TableView({ connectionId, database, table }) {
2981
+ const qc = useQueryClient();
2982
+ const { data: schema } = useSuspenseQuery(schemaQuery(connectionId, database, table));
2983
+ const [tab, setTab] = useState("data");
2984
+ const [page, setPage] = useState(0);
2985
+ const [sort, setSort] = useState();
2986
+ const [whereText, setWhereText] = useState("");
2987
+ const [where] = useDebouncedValue(whereText, { wait: 400 });
2988
+ const [selection, setSelection] = useState({
2989
+ source: void 0,
2990
+ rows: NO_SELECTION
2991
+ });
2992
+ const [adding, setAdding] = useState(false);
2993
+ const [pendingDelete, setPendingDelete] = useState(null);
2994
+ const [actionError, setActionError] = useState(null);
2995
+ const whereRef = useRef(null);
2996
+ useHotkey("Mod+Shift+F", () => whereRef.current?.focus());
2997
+ const params = {
2998
+ connectionId,
2999
+ database,
3000
+ table
3001
+ };
3002
+ const rowsOptions = rowsQuery({
3003
+ ...params,
3004
+ limit: PAGE_SIZE,
3005
+ offset: page * PAGE_SIZE,
3006
+ orderBy: sort,
3007
+ where: where.trim() || void 0
3008
+ });
3009
+ const rows = useQuery({
3010
+ ...rowsOptions,
3011
+ placeholderData: keepPreviousData
3012
+ });
3013
+ const refreshRows = () => qc.invalidateQueries({ queryKey: [
3014
+ "rows",
3015
+ connectionId,
3016
+ database,
3017
+ table
3018
+ ] });
3019
+ const update = useMutation({
3020
+ mutationFn: updateRow,
3021
+ onSuccess: refreshRows
3022
+ });
3023
+ const insert = useMutation({
3024
+ mutationFn: insertRow,
3025
+ onSuccess: refreshRows
3026
+ });
3027
+ const remove = useMutation({
3028
+ mutationFn: deleteRows,
3029
+ onSuccess: refreshRows
3030
+ });
3031
+ const columns = (rows.data?.result.columns ?? schema.columns.map((c) => ({
3032
+ name: c.name,
3033
+ type: c.type
3034
+ }))).map((c) => {
3035
+ const meta = schema.columns.find((s) => s.name === c.name);
3036
+ return {
3037
+ name: c.name,
3038
+ type: meta?.type ?? c.type,
3039
+ nullable: meta?.nullable,
3040
+ isKey: meta?.isPrimaryKey
3041
+ };
3042
+ });
3043
+ const resultRows = rows.data?.result.rows;
3044
+ const data = resultRows ?? [];
3045
+ const selected = selection.source === resultRows ? selection.rows : NO_SELECTION;
3046
+ const setSelected = (next) => setSelection({
3047
+ source: resultRows,
3048
+ rows: next
3049
+ });
3050
+ const total = rows.data?.total ?? 0;
3051
+ const editable = schema.editBlockedReason === null;
3052
+ const keyOf = (row) => Object.fromEntries(schema.keyColumns.map((k) => [k, row[columns.findIndex((c) => c.name === k)] ?? null]));
3053
+ const onCommit = async (rowIndex, column, value) => {
3054
+ const row = data[rowIndex];
3055
+ if (!row) return;
3056
+ setActionError(null);
3057
+ try {
3058
+ await update.mutateAsync({ data: {
3059
+ ...params,
3060
+ key: keyOf(row),
3061
+ changes: { [column.name]: value }
3062
+ } });
3063
+ } catch (error) {
3064
+ setActionError(errorMessage(error));
3065
+ }
3066
+ };
3067
+ const toggleSort = (column) => {
3068
+ setPage(0);
3069
+ setSelected(NO_SELECTION);
3070
+ setSort((s) => s?.column !== column ? {
3071
+ column,
3072
+ direction: "asc"
3073
+ } : s.direction === "asc" ? {
3074
+ column,
3075
+ direction: "desc"
3076
+ } : void 0);
3077
+ };
3078
+ const from = total === 0 ? 0 : page * PAGE_SIZE + 1;
3079
+ const to = page * PAGE_SIZE + data.length;
3080
+ const grid = /* @__PURE__ */ jsxs("div", {
3081
+ className: "x78zum5 xdt5ytf x98rzlu x2lwn1j",
3082
+ children: [
3083
+ /* @__PURE__ */ jsxs("div", {
3084
+ className: "x78zum5 x6s0dn4 xbbb4fa xjr6psv xwwqodo x2lah0s xso031l x1q0q8m5 x1atvb72",
3085
+ children: [
3086
+ /* @__PURE__ */ jsx("div", {
3087
+ className: "x1elwuyh x5w4yej x17fpy1y",
3088
+ children: /* @__PURE__ */ jsx(Input$1, {
3089
+ inputRef: whereRef,
3090
+ mono: true,
3091
+ "aria-label": "Row filter",
3092
+ placeholder: "WHERE …",
3093
+ value: whereText,
3094
+ onChange: (e) => {
3095
+ setWhereText(e.target.value);
3096
+ setPage(0);
3097
+ setSelected(NO_SELECTION);
3098
+ }
3099
+ })
3100
+ }),
3101
+ /* @__PURE__ */ jsx(Button$1, {
3102
+ icon: true,
3103
+ variant: "ghost",
3104
+ "aria-label": "Refresh rows",
3105
+ onClick: () => void refreshRows(),
3106
+ children: /* @__PURE__ */ jsx(Refresh, {})
3107
+ }),
3108
+ /* @__PURE__ */ jsx("div", { className: "x98rzlu" }),
3109
+ selected.size > 0 ? /* @__PURE__ */ jsxs(Button$1, {
3110
+ variant: "danger",
3111
+ onClick: () => setPendingDelete([...selected].map((i) => keyOf(data[i] ?? []))),
3112
+ children: [/* @__PURE__ */ jsx(Trash, {}), /* @__PURE__ */ jsxs("span", { children: [
3113
+ "Delete ",
3114
+ /* @__PURE__ */ jsx(Num, { children: selected.size }),
3115
+ " ",
3116
+ selected.size === 1 ? "row" : "rows"
3117
+ ] })]
3118
+ }) : null,
3119
+ /* @__PURE__ */ jsxs(Button$1, {
3120
+ disabled: !editable,
3121
+ onClick: () => setAdding(true),
3122
+ children: [/* @__PURE__ */ jsx(Plus, {}), " Add row"]
3123
+ }),
3124
+ /* @__PURE__ */ jsxs("div", {
3125
+ className: "x78zum5 x6s0dn4 xbbb4fa x2lah0s",
3126
+ children: [/* @__PURE__ */ jsxs("span", {
3127
+ ...{
3128
+ 0: { className: "xqtwvag x1tviuuy xuxw1ft x19991ni x19jr8i9 x6setmy" },
3129
+ 1: { className: "xqtwvag x1tviuuy xuxw1ft x19991ni x19jr8i9 x6setmy xbyyjgo" }
3130
+ }[!!rows.isFetching << 0],
3131
+ children: [
3132
+ /* @__PURE__ */ jsxs(Num, { children: [
3133
+ from,
3134
+ "–",
3135
+ to
3136
+ ] }),
3137
+ " ",
3138
+ "of ",
3139
+ /* @__PURE__ */ jsx(Num, { children: total }),
3140
+ " · ",
3141
+ /* @__PURE__ */ jsx(Num, { children: rows.data?.result.elapsedMs ?? 0 }),
3142
+ " ms"
3143
+ ]
3144
+ }), /* @__PURE__ */ jsxs("div", {
3145
+ className: "x78zum5 x1jnr06f",
3146
+ children: [/* @__PURE__ */ jsx(Button$1, {
3147
+ icon: true,
3148
+ "aria-label": "Previous page",
3149
+ disabled: page === 0,
3150
+ onClick: () => {
3151
+ setPage(page - 1);
3152
+ setSelected(NO_SELECTION);
3153
+ },
3154
+ children: /* @__PURE__ */ jsx(ChevronLeft, {})
3155
+ }), /* @__PURE__ */ jsx(Button$1, {
3156
+ icon: true,
3157
+ "aria-label": "Next page",
3158
+ disabled: to >= total,
3159
+ onClick: () => {
3160
+ setPage(page + 1);
3161
+ setSelected(NO_SELECTION);
3162
+ },
3163
+ children: /* @__PURE__ */ jsx(ChevronRight, {})
3164
+ })]
3165
+ })]
3166
+ })
3167
+ ]
3168
+ }),
3169
+ schema.editBlockedReason ? /* @__PURE__ */ jsxs("div", {
3170
+ className: "x1t0vds8 xjr6psv xqtwvag x1tviuuy xgwgzc4 xso031l x1q0q8m5 x1atvb72 x2lah0s",
3171
+ children: ["Read-only: ", schema.editBlockedReason]
3172
+ }) : null,
3173
+ actionError ? /* @__PURE__ */ jsx("div", {
3174
+ role: "alert",
3175
+ className: "xwwqodo xjr6psv xqtwvag xb0vdwp x1imhpfg xso031l x1q0q8m5 x1mo5f3e x126k92a x13faqbe x2lah0s",
3176
+ children: actionError
3177
+ }) : null,
3178
+ rows.isError ? /* @__PURE__ */ jsx("div", {
3179
+ role: "alert",
3180
+ className: "xwwqodo xjr6psv xqtwvag xb0vdwp x1imhpfg xso031l x1q0q8m5 x1mo5f3e x126k92a x13faqbe x2lah0s",
3181
+ children: errorMessage(rows.error)
3182
+ }) : null,
3183
+ /* @__PURE__ */ jsx("div", {
3184
+ ...{
3185
+ 0: { className: "x98rzlu x2lwn1j x78zum5 xdt5ytf x19991ni x19jr8i9" },
3186
+ 1: { className: "x98rzlu x2lwn1j x78zum5 xdt5ytf x19991ni x19jr8i9 xbyyjgo x47corl x5h36tt" }
3187
+ }[!!rows.isFetching << 0],
3188
+ children: /* @__PURE__ */ jsx(DataGrid, {
3189
+ columns,
3190
+ rows: data,
3191
+ loading: rows.isPending,
3192
+ sort,
3193
+ onSortChange: toggleSort,
3194
+ onCommit: editable ? onCommit : void 0,
3195
+ selected: editable ? selected : void 0,
3196
+ onSelectedChange: editable ? setSelected : void 0
3197
+ })
3198
+ })
3199
+ ]
3200
+ });
3201
+ const structure = /* @__PURE__ */ jsx("div", {
3202
+ className: "xysyzu8 xmjl1bo",
3203
+ children: /* @__PURE__ */ jsxs("table", {
3204
+ className: "x1mwwwfo xqtwvag",
3205
+ children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: [
3206
+ "Column",
3207
+ "Type",
3208
+ "Nullable",
3209
+ "Key",
3210
+ "Default"
3211
+ ].map((h) => /* @__PURE__ */ jsx("th", {
3212
+ className: "xdpxx8g x1t0vds8 xjr6psv x1tviuuy xk50ysn xso031l x1q0q8m5 x1orbg20",
3213
+ children: h
3214
+ }, h)) }) }), /* @__PURE__ */ jsx("tbody", { children: schema.columns.map((c) => /* @__PURE__ */ jsxs("tr", { children: [
3215
+ /* @__PURE__ */ jsx("td", {
3216
+ className: "x1t0vds8 xjr6psv xso031l x1q0q8m5 x1atvb72",
3217
+ children: c.name
3218
+ }),
3219
+ /* @__PURE__ */ jsx("td", {
3220
+ className: "x1t0vds8 xjr6psv xso031l x1q0q8m5 x1atvb72",
3221
+ children: c.type
3222
+ }),
3223
+ /* @__PURE__ */ jsx("td", {
3224
+ className: "x1t0vds8 xjr6psv xso031l x1q0q8m5 x1atvb72",
3225
+ children: c.nullable ? "yes" : "no"
3226
+ }),
3227
+ /* @__PURE__ */ jsx("td", {
3228
+ className: "x1t0vds8 xjr6psv xso031l x1q0q8m5 x1atvb72 x1tviuuy",
3229
+ children: c.isPrimaryKey ? /* @__PURE__ */ jsx("span", {
3230
+ role: "img",
3231
+ "aria-label": "Primary key",
3232
+ children: /* @__PURE__ */ jsx(Key, {})
3233
+ }) : null
3234
+ }),
3235
+ /* @__PURE__ */ jsx("td", {
3236
+ className: "x1t0vds8 xjr6psv xso031l x1q0q8m5 x1atvb72",
3237
+ children: c.default ?? ""
3238
+ })
3239
+ ] }, c.name)) })]
3240
+ })
3241
+ });
3242
+ return /* @__PURE__ */ jsxs("div", {
3243
+ className: "x78zum5 xdt5ytf x98rzlu x2lwn1j",
3244
+ children: [
3245
+ /* @__PURE__ */ jsxs("div", {
3246
+ className: "x78zum5 x17d4w8g x5md06r xgt6uh4 x2lah0s xkh2ocl x6s0dn4 xvj2dgp",
3247
+ children: [/* @__PURE__ */ jsxs("span", {
3248
+ className: "x1tviuuy",
3249
+ children: [database, " /"]
3250
+ }), /* @__PURE__ */ jsx("span", {
3251
+ className: "x1s688f",
3252
+ children: table
3253
+ })]
3254
+ }),
3255
+ /* @__PURE__ */ jsx(Tabs$1, {
3256
+ value: tab,
3257
+ onValueChange: setTab,
3258
+ items: [{
3259
+ value: "data",
3260
+ label: "Data",
3261
+ content: grid
3262
+ }, {
3263
+ value: "structure",
3264
+ label: "Structure",
3265
+ content: structure
3266
+ }]
3267
+ }),
3268
+ adding ? /* @__PURE__ */ jsx(InsertRowDialog, {
3269
+ columns: schema.columns,
3270
+ onClose: () => setAdding(false),
3271
+ onInsert: async (values) => {
3272
+ await insert.mutateAsync({ data: {
3273
+ ...params,
3274
+ values
3275
+ } });
3276
+ }
3277
+ }) : null,
3278
+ pendingDelete ? /* @__PURE__ */ jsx(ConfirmDialog, {
3279
+ title: "Delete rows",
3280
+ message: /* @__PURE__ */ jsxs(Fragment, { children: [
3281
+ "Delete ",
3282
+ /* @__PURE__ */ jsx(Num, { children: pendingDelete.length }),
3283
+ " selected row",
3284
+ pendingDelete.length === 1 ? "" : "s",
3285
+ " from ",
3286
+ database,
3287
+ ".",
3288
+ table,
3289
+ "? This cannot be undone."
3290
+ ] }),
3291
+ confirmLabel: "Delete rows",
3292
+ busy: remove.isPending,
3293
+ onCancel: () => setPendingDelete(null),
3294
+ onConfirm: async () => {
3295
+ setActionError(null);
3296
+ try {
3297
+ await remove.mutateAsync({ data: {
3298
+ ...params,
3299
+ keys: pendingDelete
3300
+ } });
3301
+ } catch (error) {
3302
+ setActionError(errorMessage(error));
3303
+ }
3304
+ setPendingDelete(null);
3305
+ }
3306
+ }) : null
3307
+ ]
3308
+ });
3309
+ }
3310
+ // src/components/tabKinds.tsx
3311
+ var tabKinds = {
3312
+ table: {
3313
+ icon: Table,
3314
+ title: (tab) => tab.target.table,
3315
+ detail: (tab, { connection }) => `${connection?.name ?? "Deleted connection"} / ${tab.target.database}.${tab.target.table}`,
3316
+ duplicable: false,
3317
+ render: (tab) => /* @__PURE__ */ jsx(TableView, {
3318
+ connectionId: tab.target.connectionId,
3319
+ database: tab.target.database,
3320
+ table: tab.target.table
3321
+ })
3322
+ },
3323
+ sql: {
3324
+ icon: Terminal,
3325
+ title: (tab) => `SQL ${tab.target.serial}`,
3326
+ detail: (tab, { connection }) => `${connection?.name ?? "Deleted connection"} / ${tab.target.database ?? connection?.database ?? "default database"}`,
3327
+ duplicable: true,
3328
+ render: (tab) => /* @__PURE__ */ jsx(SqlEditor, { tab })
3329
+ }
3330
+ };
3331
+ var kindOf = (tab) => tabKinds[tab.target.kind];
3332
+ // src/components/TabBar.tsx
3333
+ var DRAG_THRESHOLD = 4;
3334
+ var TAB_GAP = 2;
3335
+ var styles = {
3336
+ tab: {
3337
+ kVAEAm: "x1n2onr6",
3338
+ k1xSpc: "x78zum5",
3339
+ kGNEyG: "x6s0dn4",
3340
+ kOIVth: "x17d4w8g",
3341
+ kZKoxP: "x10w6t97",
3342
+ kUk6DE: "x1sz3e5v",
3343
+ k7Eaqz: "x1rp7rr8",
3344
+ kE3dHu: "x1lqa7cf",
3345
+ kpe85a: "x1ug7bdz",
3346
+ kGuDYH: "xvj2dgp",
3347
+ kMwMTN: "x1tviuuy",
3348
+ kIxVMA: "xp8sth8",
3349
+ ksF3WI: "x8qswk9",
3350
+ kMzoRj: "xmkeg23",
3351
+ ksu8eU: "x1y0btm7",
3352
+ kVAM5u: "x9r1u3d",
3353
+ kt9PQ7: "x1qhh985",
3354
+ kWkggS: "xjbqb8w x1sxlxsz",
3355
+ k1ekBW: "x15406qy",
3356
+ kIyJzY: "x19jr8i9",
3357
+ kAMwcw: "x6setmy",
3358
+ kkrTdU: "xt0e3qv",
3359
+ kfSwDN: "x87ps6o",
3360
+ kFalU9: "x1bblx7b",
3361
+ kI3sdo: "x1a2a7pz xn0s4wz",
3362
+ kInvED: "x1g40iwv",
3363
+ $$css: true
3364
+ },
3365
+ tabActive: {
3366
+ kMwMTN: "x122w267",
3367
+ kWkggS: "x185or1n",
3368
+ kVAM5u: "xpa2s8g",
3369
+ k1ekBW: "x13b0p5u",
3370
+ $$css: true
3371
+ },
3372
+ hue: (hue) => [{
3373
+ "--xm54oyi": hue != null ? "xyynxod" : hue,
3374
+ $$css: true
3375
+ }, { "--x---xm54oyi": hue != null ? hue : void 0 }],
3376
+ tabTint: {
3377
+ kWkggS: "xxhzbed x1jw1774",
3378
+ $$css: true
3379
+ },
3380
+ tabTintActive: {
3381
+ kWkggS: "x1ke9jqs",
3382
+ $$css: true
3383
+ },
3384
+ tabDragging: {
3385
+ kY2c9j: "xhtitgo",
3386
+ kWkggS: "x14fh20",
3387
+ kGVxlE: "xx50l81",
3388
+ kkrTdU: "xi9pz9s",
3389
+ k1ekBW: "x13b0p5u",
3390
+ $$css: true
3391
+ }
3392
+ };
3393
+ var _temp = {
3394
+ "x-default-marker": "x-default-marker",
3395
+ $$css: true
3396
+ };
3397
+ function TabBar() {
3398
+ const { tabs } = useWorkspace();
3399
+ const active = useActiveTab();
3400
+ const explorerId = useExplorerConnectionId();
3401
+ const actions = useWorkspaceActions();
3402
+ const connections = useQuery(connectionsQuery()).data ?? [];
3403
+ const folders = useQuery(foldersQuery()).data ?? [];
3404
+ const [draggingId, setDraggingId] = useState(null);
3405
+ const scroller = useRef(null);
3406
+ const els = useRef(/* @__PURE__ */ new Map());
3407
+ const drag = useRef(null);
3408
+ useEffect(() => {
3409
+ if (active) els.current.get(active.id)?.scrollIntoView({
3410
+ block: "nearest",
3411
+ inline: "nearest"
3412
+ });
3413
+ }, [active?.id, tabs.length]);
3414
+ const shiftSlots = (d, dx) => {
3415
+ const me = d.slots[d.index];
3416
+ if (!me) return;
3417
+ d.slots.forEach((slot, k) => {
3418
+ const el = els.current.get(slot.id);
3419
+ if (!el) return;
3420
+ let shift = 0;
3421
+ if (k === d.index) shift = dx;
3422
+ else if (k < d.index && k >= d.target) shift = me.width + TAB_GAP;
3423
+ else if (k > d.index && k <= d.target) shift = -(me.width + TAB_GAP);
3424
+ el.style.transform = shift === 0 ? "" : `translateX(${shift}px)`;
3425
+ });
3426
+ };
3427
+ const startDrag = (e, tab, index) => {
3428
+ if (e.button !== 0) return;
3429
+ if (active?.id !== tab.id) actions.activate(tab.id);
3430
+ e.currentTarget.setPointerCapture(e.pointerId);
3431
+ drag.current = {
3432
+ id: tab.id,
3433
+ index,
3434
+ pointerId: e.pointerId,
3435
+ startX: e.clientX,
3436
+ startScroll: scroller.current?.scrollLeft ?? 0,
3437
+ slots: tabs.map((t) => {
3438
+ const el = els.current.get(t.id);
3439
+ return {
3440
+ id: t.id,
3441
+ left: el?.offsetLeft ?? 0,
3442
+ width: el?.offsetWidth ?? 0
3443
+ };
3444
+ }),
3445
+ moved: false,
3446
+ target: index
3447
+ };
3448
+ };
3449
+ const moveDrag = (e) => {
3450
+ const d = drag.current;
3451
+ const me = d?.slots[d.index];
3452
+ const first = d?.slots[0];
3453
+ const last = d?.slots.at(-1);
3454
+ if (!d || !me || !first || !last || e.pointerId !== d.pointerId) return;
3455
+ const raw = e.clientX - d.startX + ((scroller.current?.scrollLeft ?? 0) - d.startScroll);
3456
+ if (!d.moved) {
3457
+ if (Math.abs(raw) < DRAG_THRESHOLD) return;
3458
+ d.moved = true;
3459
+ setDraggingId(d.id);
3460
+ }
3461
+ const dx = Math.min(Math.max(raw, first.left - me.left), last.left + last.width - (me.left + me.width));
3462
+ const center = me.left + me.width / 2 + dx;
3463
+ d.target = d.slots.filter((s) => s.id !== d.id && s.left + s.width / 2 < center).length;
3464
+ shiftSlots(d, dx);
3465
+ };
3466
+ const endDrag = (e) => {
3467
+ const d = drag.current;
3468
+ if (!d || e.pointerId !== d.pointerId) return;
3469
+ drag.current = null;
3470
+ setDraggingId(null);
3471
+ if (!d.moved) return;
3472
+ for (const el of els.current.values()) el.style.transform = "";
3473
+ if (d.target === d.index) return;
3474
+ const ids = tabs.map((t) => t.id);
3475
+ ids.splice(d.index, 1);
3476
+ ids.splice(d.target, 0, d.id);
3477
+ actions.reorder(ids);
3478
+ };
3479
+ const menuFor = (tab, index) => {
3480
+ const others = tabs.filter((t) => t.id !== tab.id).map((t) => t.id);
3481
+ const right = tabs.slice(index + 1).map((t) => t.id);
3482
+ const sql = tab.target.kind === "sql" ? {
3483
+ ...tab,
3484
+ target: tab.target
3485
+ } : null;
3486
+ return [
3487
+ {
3488
+ label: "Close",
3489
+ hint: "⌥W",
3490
+ onSelect: () => void actions.close([tab.id])
3491
+ },
3492
+ ...others.length > 0 ? [{
3493
+ label: "Close others",
3494
+ onSelect: () => void actions.close(others, tab.id)
3495
+ }] : [],
3496
+ ...right.length > 0 ? [{
3497
+ label: "Close to the right",
3498
+ onSelect: () => void actions.close(right, tab.id)
3499
+ }] : [],
3500
+ {
3501
+ label: "Close all",
3502
+ onSelect: () => void actions.close(tabs.map((t) => t.id))
3503
+ },
3504
+ ...sql && kindOf(tab).duplicable ? [{ type: "separator" }, {
3505
+ label: "Duplicate",
3506
+ onSelect: () => void actions.duplicate(sql)
3507
+ }] : []
3508
+ ];
3509
+ };
3510
+ const openNewSql = () => {
3511
+ if (explorerId) actions.openSql(explorerId);
3512
+ };
3513
+ return /* @__PURE__ */ jsxs("div", {
3514
+ className: "x78zum5 x1qjc9v5 xc9qbxq x2lah0s x1peleaf xgwgzc4 xpgesz",
3515
+ children: [/* @__PURE__ */ jsx("div", {
3516
+ ref: scroller,
3517
+ className: "xkz0k9k xeuugli xw2csxc x10wlt62 x1rohswg",
3518
+ onWheel: (e) => {
3519
+ if (e.deltaX === 0 && scroller.current) scroller.current.scrollLeft += e.deltaY;
3520
+ },
3521
+ children: /* @__PURE__ */ jsx("div", {
3522
+ role: "tablist",
3523
+ "aria-label": "Open tabs",
3524
+ className: "x1n2onr6 x78zum5 x195vfkc xuk3077 x5yr21d",
3525
+ children: tabs.map((tab, index) => {
3526
+ const connection = connections.find((c) => c.id === tab.target.connectionId);
3527
+ const tabFolder = folders.find((f) => f.id === connection?.folderId);
3528
+ const kind = kindOf(tab);
3529
+ const Icon = kind.icon;
3530
+ const title = kind.title(tab, { connection });
3531
+ const isActive = tab.id === active?.id;
3532
+ return /* @__PURE__ */ jsx(ContextMenuArea, {
3533
+ entries: menuFor(tab, index),
3534
+ children: /* @__PURE__ */ jsx(Tooltip$1, {
3535
+ label: `${title} · ${kind.detail(tab, { connection })}`,
3536
+ hint: index < 9 ? /* @__PURE__ */ jsx(Kbd, { children: `⌥${index + 1}` }) : void 0,
3537
+ children: /* @__PURE__ */ jsxs("div", {
3538
+ role: "tab",
3539
+ "aria-selected": isActive,
3540
+ tabIndex: isActive ? 0 : -1,
3541
+ ref: (el) => {
3542
+ if (el) els.current.set(tab.id, el);
3543
+ return () => {
3544
+ els.current.delete(tab.id);
3545
+ };
3546
+ },
3547
+ ...stylex.props(styles.tab, tabFolder && styles.hue(FOLDER_COLORS[tabFolder.color]), tabFolder && styles.tabTint, isActive && styles.tabActive, tabFolder && isActive && styles.tabTintActive, draggingId === tab.id && styles.tabDragging, _temp),
3548
+ onPointerDown: (e) => startDrag(e, tab, index),
3549
+ onPointerMove: moveDrag,
3550
+ onPointerUp: endDrag,
3551
+ onPointerCancel: endDrag,
3552
+ onMouseDown: (e) => {
3553
+ if (e.button === 1) e.preventDefault();
3554
+ },
3555
+ onAuxClick: (e) => {
3556
+ if (e.button === 1) actions.close([tab.id]);
3557
+ },
3558
+ onKeyDown: (e) => {
3559
+ if (e.key === "Enter" || e.key === " ") {
3560
+ e.preventDefault();
3561
+ actions.activate(tab.id);
3562
+ }
3563
+ },
3564
+ children: [
3565
+ /* @__PURE__ */ jsx("span", {
3566
+ className: "x78zum5 x1tviuuy x2lah0s",
3567
+ children: /* @__PURE__ */ jsx(Icon, {})
3568
+ }),
3569
+ /* @__PURE__ */ jsx("span", {
3570
+ className: "xeuugli x1f0l55g x2lah0s xb3r6kr xlyipyv xuxw1ft xk50ysn",
3571
+ children: title
3572
+ }),
3573
+ /* @__PURE__ */ jsx("span", {
3574
+ className: "xeuugli x1bcm92b xb3r6kr xlyipyv xuxw1ft xqtwvag x1tviuuy",
3575
+ children: connection?.name ?? "?"
3576
+ }),
3577
+ /* @__PURE__ */ jsx("button", {
3578
+ type: "button",
3579
+ "aria-label": `Close ${title}`,
3580
+ tabIndex: -1,
3581
+ ...{
3582
+ 0: { className: "x78zum5 x6s0dn4 xl56j7k x1xp8n7a xmix8c7 x8x9d4c x1717udv x2lah0s xc342km xn7wf0q x1tviuuy x1ypdohk xjbqb8w x1sp8090 x1hc1fzr x4fpvau x7stcvd x1j36eff x1nxt06a x1a2a7pz xn0s4wz" },
3583
+ 1: { className: "x78zum5 x6s0dn4 xl56j7k x1xp8n7a xmix8c7 x8x9d4c x1717udv x2lah0s xc342km xn7wf0q x1tviuuy x1ypdohk xjbqb8w x1sp8090 x1a2a7pz xn0s4wz x1hc1fzr" }
3584
+ }[!!isActive << 0],
3585
+ onPointerDown: (e) => e.stopPropagation(),
3586
+ onClick: (e) => {
3587
+ e.stopPropagation();
3588
+ actions.close([tab.id]);
3589
+ },
3590
+ children: /* @__PURE__ */ jsx(Close, {})
3591
+ })
3592
+ ]
3593
+ })
3594
+ })
3595
+ }, tab.id);
3596
+ })
3597
+ })
3598
+ }), /* @__PURE__ */ jsx("div", {
3599
+ className: "x78zum5 x6s0dn4 xu7byfh x2lah0s",
3600
+ children: /* @__PURE__ */ jsx(Tooltip$1, {
3601
+ label: "New SQL tab",
3602
+ hint: /* @__PURE__ */ jsx(Kbd, { children: "⌥T" }),
3603
+ children: /* @__PURE__ */ jsx(Button$1, {
3604
+ variant: "ghost",
3605
+ icon: true,
3606
+ small: true,
3607
+ "aria-label": "New SQL tab",
3608
+ disabled: !explorerId,
3609
+ onClick: openNewSql,
3610
+ children: /* @__PURE__ */ jsx(Plus, {})
3611
+ })
3612
+ })
3613
+ })]
3614
+ });
3615
+ }
3616
+ // src/components/TabContents.tsx
3617
+ function TabPane({ tab }) {
3618
+ return /* @__PURE__ */ jsx("div", {
3619
+ role: "tabpanel",
3620
+ className: "x78zum5 xdt5ytf x98rzlu x2lwn1j xeuugli",
3621
+ children: /* @__PURE__ */ jsx(CatchBoundary, {
3622
+ getResetKey: () => tab.id,
3623
+ errorComponent: ({ error }) => /* @__PURE__ */ jsx(EmptyState, {
3624
+ title: "Could not open tab",
3625
+ hint: errorMessage(error),
3626
+ danger: true
3627
+ }),
3628
+ children: /* @__PURE__ */ jsx(Suspense, {
3629
+ fallback: /* @__PURE__ */ jsx(PendingPane, {}),
3630
+ children: kindOf(tab).render(tab)
3631
+ })
3632
+ })
3633
+ });
3634
+ }
3635
+ /**
3636
+ * Every open tab stays mounted. Hidden ones keep their state, scroll and DOM, and React
3637
+ * suspends their effects, so a hidden tab runs nothing and fetches nothing.
3638
+ */
3639
+ function TabContents() {
3640
+ const { tabs } = useWorkspace();
3641
+ const active = useActiveTab();
3642
+ return tabs.map((tab) => /* @__PURE__ */ jsx(Activity, {
3643
+ mode: tab.id === active?.id ? "visible" : "hidden",
3644
+ children: /* @__PURE__ */ jsx(TabPane, { tab })
3645
+ }, tab.id));
3646
+ }
3647
+ // src/components/Workbench.tsx
3648
+ var DIGITS = [
3649
+ 1,
3650
+ 2,
3651
+ 3,
3652
+ 4,
3653
+ 5,
3654
+ 6,
3655
+ 7,
3656
+ 8,
3657
+ 9
3658
+ ];
3659
+ function Workbench() {
3660
+ const workspace = useWorkspace();
3661
+ const active = useActiveTab();
3662
+ const explorerId = useExplorerConnectionId();
3663
+ const connections = useQuery(connectionsQuery());
3664
+ const actions = useWorkspaceActions();
3665
+ const connection = connections.data?.find((c) => c.id === explorerId);
3666
+ useEffect(() => {
3667
+ if (active && workspace.activeTabId !== active.id) actions.markActive(active.id);
3668
+ });
3669
+ const { tabs } = workspace;
3670
+ const goTo = (offset) => {
3671
+ const at = Math.max(tabs.findIndex((t) => t.id === active?.id), 0);
3672
+ const next = tabs.at((at + offset) % tabs.length);
3673
+ if (next) actions.activate(next.id);
3674
+ };
3675
+ useHotkeys([
3676
+ {
3677
+ hotkey: "Alt+[KeyT]",
3678
+ callback: () => {
3679
+ if (connection) actions.openSql(connection.id);
3680
+ }
3681
+ },
3682
+ {
3683
+ hotkey: "Alt+[KeyW]",
3684
+ callback: () => {
3685
+ if (active) actions.close([active.id]);
3686
+ }
3687
+ },
3688
+ {
3689
+ hotkey: "Alt+[BracketRight]",
3690
+ callback: () => goTo(1)
3691
+ },
3692
+ {
3693
+ hotkey: "Alt+[BracketLeft]",
3694
+ callback: () => goTo(-1)
3695
+ },
3696
+ ...DIGITS.map((n) => ({
3697
+ hotkey: `Alt+[Digit${n}]`,
3698
+ callback: () => {
3699
+ const tab = tabs[n - 1];
3700
+ if (tab) actions.activate(tab.id);
3701
+ }
3702
+ }))
3703
+ ], { ignoreInputs: false });
3704
+ return /* @__PURE__ */ jsxs("main", {
3705
+ className: "x98rzlu xeuugli x78zum5 x5yr21d",
3706
+ children: [connection ? /* @__PURE__ */ jsx(Explorer, {
3707
+ connection,
3708
+ activeTable: active?.target.kind === "table" ? active.target : null
3709
+ }, connection.id) : null, /* @__PURE__ */ jsxs("div", {
3710
+ className: "x98rzlu xeuugli x78zum5 xdt5ytf x5yr21d",
3711
+ children: [tabs.length > 0 ? /* @__PURE__ */ jsx(TabBar, {}) : null, /* @__PURE__ */ jsxs("div", {
3712
+ className: "x98rzlu x2lwn1j x78zum5 xdt5ytf",
3713
+ children: [/* @__PURE__ */ jsx(TabContents, {}), /* @__PURE__ */ jsx(Outlet, {})]
3714
+ })]
3715
+ })]
3716
+ });
3717
+ }
3718
+ // src/components/TopProgress.tsx
3719
+ function TopProgress() {
3720
+ const navigating = useRouterState({ select: (s) => s.status === "pending" });
3721
+ const fetching = useIsFetching() > 0;
3722
+ if (!navigating && !fetching) return null;
3723
+ return /* @__PURE__ */ jsx("div", {
3724
+ role: "progressbar",
3725
+ "aria-label": "Loading",
3726
+ className: "xixxii4 x13vifvy xu96u03 x3m8u43 x36qwtl xb3r6kr x47corl xfo81ep xqcmdr3 x12l1aco xmmqq2k xskzprw",
3727
+ children: /* @__PURE__ */ jsx("div", { className: "xxljpkc x5yr21d xq83s4c xr9n2wq xr7ufhj x4hg4is xa4qsjk" })
3728
+ });
3729
+ }
3730
+ // src/styles.css?url
3731
+ var styles_default = "/assets/styles-CdkLyHdd.css";
3732
+ // src/routes/__root.tsx
3733
+ var Route$6 = createRootRouteWithContext()({
3734
+ head: () => ({
3735
+ meta: [
3736
+ { charSet: "utf-8" },
3737
+ {
3738
+ name: "viewport",
3739
+ content: "width=device-width, initial-scale=1"
3740
+ },
3741
+ { title: "db-player" }
3742
+ ],
3743
+ links: [
3744
+ {
3745
+ rel: "icon",
3746
+ href: "/favicon.svg",
3747
+ type: "image/svg+xml"
3748
+ },
3749
+ {
3750
+ rel: "icon",
3751
+ href: "/favicon-32.png",
3752
+ type: "image/png",
3753
+ sizes: "32x32"
3754
+ },
3755
+ {
3756
+ rel: "apple-touch-icon",
3757
+ href: "/apple-touch-icon.png"
3758
+ },
3759
+ {
3760
+ rel: "stylesheet",
3761
+ href: styles_default
3762
+ },
3763
+ ...[]
3764
+ ]
3765
+ }),
3766
+ loader: async ({ context }) => {
3767
+ const [theme, layout] = await Promise.all([
3768
+ getTheme(),
3769
+ getLayout(),
3770
+ context.queryClient.ensureQueryData(foldersQuery()),
3771
+ context.queryClient.ensureQueryData(connectionsQuery()),
3772
+ context.queryClient.ensureQueryData(workspaceQuery())
3773
+ ]);
3774
+ return {
3775
+ theme,
3776
+ layout
3777
+ };
3778
+ },
3779
+ component: RootComponent
3780
+ });
3781
+ function RootComponent() {
3782
+ const { theme, layout } = Route$6.useLoaderData();
3783
+ useEffect(() => {}, []);
3784
+ return /* @__PURE__ */ jsxs("html", {
3785
+ lang: "en",
3786
+ "data-theme": theme === "system" ? void 0 : theme,
3787
+ ...stylex.props(theme === "light" && lightTheme, theme === "dark" && darkTheme),
3788
+ children: [/* @__PURE__ */ jsx("head", { children: /* @__PURE__ */ jsx(HeadContent, {}) }), /* @__PURE__ */ jsxs("body", {
3789
+ className: "x134rb1s x185or1n x122w267",
3790
+ children: [/* @__PURE__ */ jsx(LayoutProvider, {
3791
+ initial: layout,
3792
+ children: /* @__PURE__ */ jsxs(TooltipProvider, { children: [/* @__PURE__ */ jsx(TopProgress, {}), /* @__PURE__ */ jsxs("div", {
3793
+ className: "x78zum5 xdt5ytf x1dr59a3 xb3r6kr",
3794
+ children: [/* @__PURE__ */ jsx(TopBar, {}), /* @__PURE__ */ jsxs("div", {
3795
+ className: "x98rzlu x2lwn1j x78zum5",
3796
+ children: [/* @__PURE__ */ jsx(Sidebar, {}), /* @__PURE__ */ jsx(Workbench, {})]
3797
+ })]
3798
+ })] })
3799
+ }), /* @__PURE__ */ jsx(Scripts, {})]
3800
+ })]
3801
+ });
3802
+ }
3803
+ // src/routes/index.tsx
3804
+ var $$splitComponentImporter$2 = () => import("./routes-Bk_zy8AX.js");
3805
+ var Route$5 = createFileRoute("/")({
3806
+ beforeLoad: async ({ context }) => {
3807
+ const { activeTabId } = await context.queryClient.ensureQueryData(workspaceQuery());
3808
+ if (activeTabId) throw redirect({
3809
+ to: "/t/$tabId",
3810
+ params: { tabId: activeTabId }
3811
+ });
3812
+ },
3813
+ component: lazyRouteComponent($$splitComponentImporter$2, "component")
3814
+ });
3815
+ // src/server/readonly.ts
3816
+ var ALLOWED_FIRST = /* @__PURE__ */ new Set([
3817
+ "select",
3818
+ "with",
3819
+ "show",
3820
+ "describe",
3821
+ "desc",
3822
+ "explain"
3823
+ ]);
3824
+ var ReadOnlyViolation = class extends Error {};
3825
+ /**
3826
+ * Splits `sql` into statements at top-level semicolons and lists each statement's words, with
3827
+ * comments dropped and string, quoted-identifier and backtick contents blanked out.
3828
+ */
3829
+ function scan(sql) {
3830
+ const statements = [];
3831
+ const words = [];
3832
+ let text = "";
3833
+ let current = [];
3834
+ let word = "";
3835
+ const endWord = () => {
3836
+ if (word) current.push(word.toLowerCase());
3837
+ word = "";
3838
+ };
3839
+ const endStatement = () => {
3840
+ endWord();
3841
+ if (current.length > 0 || text.trim()) {
3842
+ statements.push(text.trim());
3843
+ words.push(current);
3844
+ }
3845
+ text = "";
3846
+ current = [];
3847
+ };
3848
+ let i = 0;
3849
+ while (i < sql.length) {
3850
+ const c = sql[i];
3851
+ const next = sql[i + 1];
3852
+ if (c === "/" && next === "*") {
3853
+ if (sql[i + 2] === "!") throw new ReadOnlyViolation("Executable comments (/*! ... */) are not allowed in read-only queries.");
3854
+ const end = sql.indexOf("*/", i + 2);
3855
+ if (end === -1) throw new ReadOnlyViolation("Unterminated block comment.");
3856
+ endWord();
3857
+ text += " ";
3858
+ i = end + 2;
3859
+ continue;
3860
+ }
3861
+ if (c === "#" || c === "-" && next === "-") {
3862
+ const end = sql.indexOf("\n", i);
3863
+ endWord();
3864
+ text += " ";
3865
+ i = end === -1 ? sql.length : end + 1;
3866
+ continue;
3867
+ }
3868
+ if (c === "'" || c === "\"" || c === "`") {
3869
+ endWord();
3870
+ let j = i + 1;
3871
+ for (; j < sql.length; j++) if (sql[j] === "\\" && c !== "`") j++;
3872
+ else if (sql[j] === c) break;
3873
+ if (j >= sql.length) throw new ReadOnlyViolation("Unterminated string or quoted name.");
3874
+ text += sql.slice(i, j + 1);
3875
+ current.push(c === "'" ? "''" : c);
3876
+ i = j + 1;
3877
+ continue;
3878
+ }
3879
+ if (c === ";") {
3880
+ endStatement();
3881
+ i++;
3882
+ continue;
3883
+ }
3884
+ if (/[\p{L}\p{N}_$]/u.test(c)) word += c;
3885
+ else endWord();
3886
+ text += c;
3887
+ i++;
3888
+ }
3889
+ endStatement();
3890
+ return {
3891
+ statements,
3892
+ words
3893
+ };
3894
+ }
3895
+ /** Throws a `ReadOnlyViolation` unless `sql` is one read-only statement. Returns it without a trailing `;`. */
3896
+ function assertReadOnlySql(sql) {
3897
+ const { statements, words } = scan(sql);
3898
+ if (statements.length === 0) throw new ReadOnlyViolation("The query is empty.");
3899
+ if (statements.length > 1) throw new ReadOnlyViolation("Only one statement is allowed per query.");
3900
+ const tokens = words[0];
3901
+ const first = tokens[0];
3902
+ if (!first || !ALLOWED_FIRST.has(first)) throw new ReadOnlyViolation(`Read-only queries must start with SELECT, WITH, SHOW, DESCRIBE, DESC or EXPLAIN (got ${first && /^[a-z]/.test(first) ? first.toUpperCase() : "something else"}).`);
3903
+ tokens.forEach((t, i) => {
3904
+ if (t === "into" && (tokens[i + 1] === "outfile" || tokens[i + 1] === "dumpfile")) throw new ReadOnlyViolation("SELECT ... INTO OUTFILE/DUMPFILE is not allowed.");
3905
+ });
3906
+ return statements[0];
3907
+ }
3908
+ /** The only way a query reaches a database without the app's own say-so: pre-check, then a driver refusing writes. */
3909
+ function runReadOnlyQuery(conn, sql, opts) {
3910
+ return drivers[conn.kind].query(conn, assertReadOnlySql(sql), {
3911
+ ...opts,
3912
+ readOnly: true
3913
+ });
3914
+ }
3915
+ // src/server/mcp.ts
3916
+ var ROW_CAP = 200;
3917
+ var CELL_CAP = 500;
3918
+ var digest = (s) => createHash("sha256").update(s).digest();
3919
+ function authorized(request) {
3920
+ const header = request.headers.get("authorization") ?? "";
3921
+ const match = /^Bearer (.+)$/i.exec(header);
3922
+ return match !== null && timingSafeEqual(digest(match[1]), digest(getMcpToken()));
3923
+ }
3924
+ var text = (body, isError = false) => ({
3925
+ content: [{
3926
+ type: "text",
3927
+ text: body
3928
+ }],
3929
+ ...isError ? { isError: true } : {}
3930
+ });
3931
+ function resolveConnection(ref) {
3932
+ const all = listConnections();
3933
+ const byId = all.find((c) => c.id === ref);
3934
+ if (byId) return byId;
3935
+ const byName = all.filter((c) => c.name === ref);
3936
+ if (byName.length === 1) return byName[0];
3937
+ if (byName.length > 1) throw new Error(`Several connections are named "${ref}". Use one of these ids: ${byName.map((c) => c.id).join(", ")}.`);
3938
+ throw new Error(`No connection with id or name "${ref}". Call list_connections to see them.`);
3939
+ }
3940
+ function cellText(cell) {
3941
+ const flat = (cell === null ? "NULL" : typeof cell === "object" ? JSON.stringify(cell) : String(cell)).replace(/\t/g, "\\t").replace(/\r?\n/g, "\\n");
3942
+ return flat.length > CELL_CAP ? `${flat.slice(0, CELL_CAP)}...[truncated, ${flat.length} chars]` : flat;
3943
+ }
3944
+ function formatResult(result) {
3945
+ return [
3946
+ `columns: ${result.columns.map((c) => `${c.name} (${c.type})`).join(", ") || "(none)"}`,
3947
+ `rows: ${result.rows.length}${result.truncated ? ` (truncated: more rows exist beyond the first ${ROW_CAP}; add LIMIT, WHERE or aggregate)` : ""}`,
3948
+ "",
3949
+ result.columns.map((c) => cellText(c.name)).join(" "),
3950
+ ...result.rows.map((row) => row.map(cellText).join(" "))
3951
+ ].join("\n");
3952
+ }
3953
+ var SCHEMA_HINT = "Discover schema with SHOW DATABASES / SHOW TABLES / DESCRIBE <table> or by querying information_schema (MySQL) or system.databases, system.tables, system.columns (ClickHouse).";
3954
+ function createServer() {
3955
+ const server = new McpServer({
3956
+ name: "db-player",
3957
+ version: "1.0.0"
3958
+ });
3959
+ server.registerTool("list_connections", {
3960
+ description: "List the saved database connections you can query. Returns id, name, kind (mysql or clickhouse), folder name and default database. Pass an id or name to run_query.",
3961
+ annotations: {
3962
+ readOnlyHint: true,
3963
+ openWorldHint: false
3964
+ }
3965
+ }, () => {
3966
+ const folders = new Map(listFolders().map((f) => [f.id, f.name]));
3967
+ const rows = listConnections().map((c) => ({
3968
+ id: c.id,
3969
+ name: c.name,
3970
+ kind: c.kind,
3971
+ folder: c.folderId && folders.get(c.folderId) || null,
3972
+ database: c.database || null
3973
+ }));
3974
+ return text(JSON.stringify(rows, null, 2));
3975
+ });
3976
+ server.registerTool("run_query", {
3977
+ description: `Run one read-only SQL statement (SELECT, WITH, SHOW, DESCRIBE, DESC or EXPLAIN) on a saved connection. Writes, DDL and multiple statements are rejected. At most ${ROW_CAP} rows come back, so aggregate or add LIMIT. ${SCHEMA_HINT}`,
3978
+ inputSchema: {
3979
+ connection: z.string().min(1).describe("Connection id or exact name from list_connections."),
3980
+ database: z.string().min(1).optional().describe("Database to run in. Defaults to the connection's default database."),
3981
+ sql: z.string().min(1).describe("A single read-only statement.")
3982
+ },
3983
+ annotations: {
3984
+ readOnlyHint: true,
3985
+ openWorldHint: false
3986
+ }
3987
+ }, async ({ connection, database, sql }) => {
3988
+ try {
3989
+ return text(formatResult(await runReadOnlyQuery(resolveConnection(connection), sql, {
3990
+ database: database ?? null,
3991
+ limit: ROW_CAP
3992
+ })));
3993
+ } catch (error) {
3994
+ const message = error instanceof Error ? error.message : String(error);
3995
+ return text(error instanceof ReadOnlyViolation ? `Rejected: ${message}` : message, true);
3996
+ }
3997
+ });
3998
+ return server;
3999
+ }
4000
+ async function handleMcpRequest(request) {
4001
+ if (!authorized(request)) return new Response(JSON.stringify({ error: "Unauthorized" }), {
4002
+ status: 401,
4003
+ headers: {
4004
+ "content-type": "application/json",
4005
+ "www-authenticate": "Bearer"
4006
+ }
4007
+ });
4008
+ const server = createServer();
4009
+ const transport = new WebStandardStreamableHTTPServerTransport({
4010
+ sessionIdGenerator: void 0,
4011
+ enableJsonResponse: true
4012
+ });
4013
+ await server.connect(transport);
4014
+ return transport.handleRequest(request);
4015
+ }
4016
+ // src/routes/mcp.ts
4017
+ var Route$4 = createFileRoute("/mcp")({ server: { handlers: { ANY: ({ request }) => handleMcpRequest(request) } } });
4018
+ // src/routes/c.$connectionId.tsx
4019
+ var $$splitComponentImporter$1 = () => import("./c._connectionId-4zi7Sa20.js");
4020
+ /** A connection with no tab: the workbench shows its Explorer and this prompt. */
4021
+ var Route$3 = createFileRoute("/c/$connectionId")({
4022
+ loader: ({ context, params }) => context.queryClient.prefetchQuery(databasesQuery(params.connectionId)),
4023
+ component: lazyRouteComponent($$splitComponentImporter$1, "component")
4024
+ });
4025
+ // src/routes/t.$tabId.tsx
4026
+ var $$splitComponentImporter = () => import("./t._tabId-Cf1urQNG.js");
4027
+ /** The tab itself renders in the workbench, so every open tab can stay mounted. */
4028
+ var Route$2 = createFileRoute("/t/$tabId")({
4029
+ pendingMs: Number.POSITIVE_INFINITY,
4030
+ beforeLoad: async ({ context, params }) => {
4031
+ if (!(await context.queryClient.ensureQueryData(workspaceQuery())).tabs.some((t) => t.id === params.tabId)) throw redirect({ to: "/" });
4032
+ },
4033
+ loader: async ({ context, params }) => {
4034
+ const tab = context.queryClient.getQueryData(workspaceQuery().queryKey)?.tabs.find((t) => t.id === params.tabId);
4035
+ if (!tab) return;
4036
+ const { target } = tab;
4037
+ await Promise.all([context.queryClient.prefetchQuery(databasesQuery(target.connectionId)), target.kind === "table" ? context.queryClient.prefetchQuery(schemaQuery(target.connectionId, target.database, target.table)) : void 0]);
4038
+ },
4039
+ component: lazyRouteComponent($$splitComponentImporter, "component")
4040
+ });
4041
+ // src/routes/c.$connectionId.sql.tsx
4042
+ /** The old SQL editor URL focuses the connection's first SQL tab, or opens one. */
4043
+ var Route$1 = createFileRoute("/c/$connectionId/sql")({ beforeLoad: async ({ context, params, preload }) => {
4044
+ if (preload) return;
4045
+ const existing = (await context.queryClient.ensureQueryData(workspaceQuery())).tabs.find((t) => t.target.kind === "sql" && t.target.connectionId === params.connectionId);
4046
+ if (existing) throw redirect({
4047
+ to: "/t/$tabId",
4048
+ params: { tabId: existing.id },
4049
+ replace: true
4050
+ });
4051
+ const opened = await openTab({ data: { target: {
4052
+ kind: "sql",
4053
+ connectionId: params.connectionId,
4054
+ database: null
4055
+ } } });
4056
+ context.queryClient.setQueryData(workspaceQuery().queryKey, opened.workspace);
4057
+ throw redirect({
4058
+ to: "/t/$tabId",
4059
+ params: { tabId: opened.tabId },
4060
+ replace: true
4061
+ });
4062
+ } });
4063
+ // src/routes/c.$connectionId.$database.$table.tsx
4064
+ /** Old table URLs open or focus the table's tab. */
4065
+ var Route = createFileRoute("/c/$connectionId/$database/$table")({ beforeLoad: async ({ context, params, preload }) => {
4066
+ if (preload) return;
4067
+ const { workspace, tabId } = await openTab({ data: { target: {
4068
+ kind: "table",
4069
+ ...params
4070
+ } } });
4071
+ context.queryClient.setQueryData(workspaceQuery().queryKey, workspace);
4072
+ throw redirect({
4073
+ to: "/t/$tabId",
4074
+ params: { tabId },
4075
+ replace: true
4076
+ });
4077
+ } });
4078
+ // src/routeTree.gen.ts
4079
+ var IndexRoute = Route$5.update({
4080
+ id: "/",
4081
+ path: "/",
4082
+ getParentRoute: () => Route$6
4083
+ });
4084
+ var McpRoute = Route$4.update({
4085
+ id: "/mcp",
4086
+ path: "/mcp",
4087
+ getParentRoute: () => Route$6
4088
+ });
4089
+ var CConnectionIdRoute = Route$3.update({
4090
+ id: "/c/$connectionId",
4091
+ path: "/c/$connectionId",
4092
+ getParentRoute: () => Route$6
4093
+ });
4094
+ var TTabIdRoute = Route$2.update({
4095
+ id: "/t/$tabId",
4096
+ path: "/t/$tabId",
4097
+ getParentRoute: () => Route$6
4098
+ });
4099
+ var CConnectionIdRouteChildren = {
4100
+ CConnectionIdSqlRoute: Route$1.update({
4101
+ id: "/sql",
4102
+ path: "/sql",
4103
+ getParentRoute: () => CConnectionIdRoute
4104
+ }),
4105
+ CConnectionIdDatabaseTableRoute: Route.update({
4106
+ id: "/$database/$table",
4107
+ path: "/$database/$table",
4108
+ getParentRoute: () => CConnectionIdRoute
4109
+ })
4110
+ };
4111
+ var rootRouteChildren = {
4112
+ IndexRoute,
4113
+ McpRoute,
4114
+ CConnectionIdRoute: CConnectionIdRoute._addFileChildren(CConnectionIdRouteChildren),
4115
+ TTabIdRoute
4116
+ };
4117
+ var routeTree = Route$6._addFileChildren(rootRouteChildren)._addFileTypes();
4118
+ // src/router.tsx
4119
+ var router_exports = /* @__PURE__ */ __exportAll({ getRouter: () => getRouter });
4120
+ function getRouter() {
4121
+ const queryClient = new QueryClient({ defaultOptions: { queries: {
4122
+ staleTime: 15e3,
4123
+ refetchOnWindowFocus: false
4124
+ } } });
4125
+ const router = createRouter({
4126
+ routeTree,
4127
+ context: { queryClient },
4128
+ scrollRestoration: true,
4129
+ defaultPreload: "intent",
4130
+ defaultPendingMs: 200,
4131
+ defaultPendingMinMs: 300,
4132
+ defaultPendingComponent: PendingPane
4133
+ });
4134
+ setupRouterSsrQueryIntegration({
4135
+ router,
4136
+ queryClient
4137
+ });
4138
+ return router;
4139
+ }
4140
+ export { getRouter, Button$1 as i, Route$3 as n, ConnectionDialog as r, router_exports as t };