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