db-player 0.0.0-stage → 0.4.1

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