@hienlh/ppm 0.9.67 → 0.9.68
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/CHANGELOG.md +6 -0
- package/dist/web/assets/{chat-tab-onkz52iv.js → chat-tab-rHbqenEa.js} +1 -1
- package/dist/web/assets/code-editor-C34pPIaA.js +8 -0
- package/dist/web/assets/database-viewer-8Eifo5Ak.js +2 -0
- package/dist/web/assets/{diff-viewer-eFO08m_L.js → diff-viewer-CrUaMMwc.js} +1 -1
- package/dist/web/assets/{extension-webview-4CL9kCKR.js → extension-webview-EJvlbzMw.js} +1 -1
- package/dist/web/assets/{git-graph-BqeE_o17.js → git-graph-boq7GWXZ.js} +1 -1
- package/dist/web/assets/index-C4vNxaKi.css +2 -0
- package/dist/web/assets/{index-DwrCg0TN.js → index-DHr-SBBY.js} +4 -4
- package/dist/web/assets/keybindings-store-CWfygy_t.js +1 -0
- package/dist/web/assets/{markdown-renderer-BUqab2os.js → markdown-renderer-BrW4qEBv.js} +1 -1
- package/dist/web/assets/{port-forwarding-tab-CfO-UJ84.js → port-forwarding-tab-UB_l73FZ.js} +1 -1
- package/dist/web/assets/{postgres-viewer-BVJZ44eU.js → postgres-viewer-BZE43hDP.js} +1 -1
- package/dist/web/assets/{settings-tab-C6hdJujW.js → settings-tab-DPx50bZm.js} +1 -1
- package/dist/web/assets/{sql-query-editor-OhZa4Z9F.js → sql-query-editor-CPBBvi1U.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-C8p1_jz4.js → sqlite-viewer-DbHvVTSs.js} +1 -1
- package/dist/web/assets/{terminal-tab-CaO0WnIo.js → terminal-tab-Br_Zlw2Z.js} +1 -1
- package/dist/web/assets/{use-monaco-theme-U9ZhfvHB.js → use-monaco-theme-DDZwQjEv.js} +1 -1
- package/dist/web/index.html +2 -2
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/src/web/components/database/connection-list.tsx +16 -10
- package/src/web/components/database/data-grid.tsx +28 -37
- package/src/web/components/database/database-viewer.tsx +27 -5
- package/src/web/components/database/use-database.ts +16 -1
- package/src/web/components/editor/code-editor.tsx +30 -30
- package/dist/web/assets/code-editor-BixOXePn.js +0 -8
- package/dist/web/assets/database-viewer-DfLe8ewt.js +0 -2
- package/dist/web/assets/index-BWLy2h18.css +0 -2
- package/dist/web/assets/keybindings-store-BNBONtSd.js +0 -1
|
@@ -21,6 +21,8 @@ interface DataGridProps {
|
|
|
21
21
|
selectedTable?: string | null;
|
|
22
22
|
selectedSchema?: string;
|
|
23
23
|
connectionName?: string;
|
|
24
|
+
/** Controlled column ILIKE filters — parent owns state */
|
|
25
|
+
columnFilters?: Record<string, string>;
|
|
24
26
|
/** Called when column ILIKE filters change — parent builds WHERE clause */
|
|
25
27
|
onColumnFilter?: (filters: Record<string, string>) => void;
|
|
26
28
|
}
|
|
@@ -29,7 +31,7 @@ export function DataGrid({
|
|
|
29
31
|
tableData, schema, loading, page, onPageChange, onCellUpdate, onRowDelete,
|
|
30
32
|
orderBy, orderDir, onToggleSort,
|
|
31
33
|
onBulkDelete, onInsertRow,
|
|
32
|
-
connectionId, selectedTable, selectedSchema, connectionName, onColumnFilter,
|
|
34
|
+
connectionId, selectedTable, selectedSchema, connectionName, columnFilters: colFilters = {}, onColumnFilter,
|
|
33
35
|
}: DataGridProps) {
|
|
34
36
|
const [editingCell, setEditingCell] = useState<{ rowIdx: number; col: string } | null>(null);
|
|
35
37
|
const [editValue, setEditValue] = useState("");
|
|
@@ -52,7 +54,6 @@ export function DataGrid({
|
|
|
52
54
|
}, [openTab]);
|
|
53
55
|
const [pinnedCols, setPinnedCols] = useState<Set<string>>(new Set());
|
|
54
56
|
const [pinnedRows, setPinnedRows] = useState<Set<number>>(new Set());
|
|
55
|
-
const [colFilters, setColFilters] = useState<Record<string, string>>({});
|
|
56
57
|
const [filterOpenCol, setFilterOpenCol] = useState<string | null>(null);
|
|
57
58
|
|
|
58
59
|
const pkCol = useMemo(() => {
|
|
@@ -118,20 +119,9 @@ export function DataGrid({
|
|
|
118
119
|
}, []);
|
|
119
120
|
|
|
120
121
|
const updateColFilter = useCallback((col: string, val: string) => {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return next;
|
|
125
|
-
});
|
|
126
|
-
}, []);
|
|
127
|
-
|
|
128
|
-
// Notify parent when column filters change
|
|
129
|
-
const colFiltersRef = useRef(colFilters);
|
|
130
|
-
useEffect(() => {
|
|
131
|
-
if (colFiltersRef.current !== colFilters) {
|
|
132
|
-
colFiltersRef.current = colFilters;
|
|
133
|
-
onColumnFilter?.(colFilters);
|
|
134
|
-
}
|
|
122
|
+
const next = { ...colFilters };
|
|
123
|
+
if (val) next[col] = val; else delete next[col];
|
|
124
|
+
onColumnFilter?.(next);
|
|
135
125
|
}, [colFilters, onColumnFilter]);
|
|
136
126
|
|
|
137
127
|
const toggleRowSelection = useCallback((idx: number) => {
|
|
@@ -219,24 +209,13 @@ export function DataGrid({
|
|
|
219
209
|
return () => el.removeEventListener("keydown", handler);
|
|
220
210
|
}, [tableData, selectedRows]);
|
|
221
211
|
|
|
222
|
-
if (!tableData) {
|
|
223
|
-
return (
|
|
224
|
-
<div className="flex items-center justify-center h-full text-xs text-muted-foreground">
|
|
225
|
-
{loading ? <Loader2 className="size-4 animate-spin" /> : "Select a table"}
|
|
226
|
-
</div>
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const totalPages = Math.ceil(tableData.total / tableData.limit) || 1;
|
|
231
|
-
const hasSelection = selectedRows.size > 0;
|
|
232
|
-
const allSelected = selectedRows.size === tableData.rows.length && tableData.rows.length > 0;
|
|
233
|
-
|
|
234
212
|
// Build ordered column list: pinned first, then unpinned
|
|
235
213
|
const orderedCols = useMemo(() => {
|
|
214
|
+
if (!tableData) return [];
|
|
236
215
|
const pinned = tableData.columns.filter((c) => pinnedCols.has(c));
|
|
237
216
|
const unpinned = tableData.columns.filter((c) => !pinnedCols.has(c));
|
|
238
217
|
return [...pinned, ...unpinned];
|
|
239
|
-
}, [tableData
|
|
218
|
+
}, [tableData?.columns, pinnedCols]);
|
|
240
219
|
|
|
241
220
|
// Measure actual column widths and header height from DOM
|
|
242
221
|
const theadRef = useRef<HTMLTableSectionElement>(null);
|
|
@@ -248,12 +227,10 @@ export function DataGrid({
|
|
|
248
227
|
if (!thead) return;
|
|
249
228
|
const measure = () => {
|
|
250
229
|
setHeaderHeight(thead.offsetHeight);
|
|
251
|
-
// Measure each <th> by data-col attribute
|
|
252
230
|
const widths = new Map<string, number>();
|
|
253
231
|
thead.querySelectorAll<HTMLElement>("th[data-col]").forEach((th) => {
|
|
254
232
|
widths.set(th.dataset.col!, th.offsetWidth);
|
|
255
233
|
});
|
|
256
|
-
// Also measure checkbox th
|
|
257
234
|
const cbTh = thead.querySelector<HTMLElement>("th[data-col='_cb']");
|
|
258
235
|
if (cbTh) widths.set("_cb", cbTh.offsetWidth);
|
|
259
236
|
setColWidths(widths);
|
|
@@ -262,7 +239,7 @@ export function DataGrid({
|
|
|
262
239
|
const obs = new ResizeObserver(measure);
|
|
263
240
|
obs.observe(thead);
|
|
264
241
|
return () => obs.disconnect();
|
|
265
|
-
}, [tableData?.columns, pinnedCols]);
|
|
242
|
+
}, [tableData?.columns, pinnedCols]);
|
|
266
243
|
|
|
267
244
|
// Compute sticky left offsets from measured widths
|
|
268
245
|
const pinnedColOffsets = useMemo(() => {
|
|
@@ -290,13 +267,11 @@ export function DataGrid({
|
|
|
290
267
|
else pinnedRowRefs.current.delete(idx);
|
|
291
268
|
}, []);
|
|
292
269
|
|
|
293
|
-
// Measure pinned rows after render
|
|
294
270
|
useEffect(() => {
|
|
295
271
|
if (pinnedRowData.length === 0) {
|
|
296
272
|
if (pinnedRowHeights.size > 0) setPinnedRowHeights(new Map());
|
|
297
273
|
return;
|
|
298
274
|
}
|
|
299
|
-
// Use rAF to measure after browser layout
|
|
300
275
|
const id = requestAnimationFrame(() => {
|
|
301
276
|
const heights = new Map<number, number>();
|
|
302
277
|
for (const { idx } of pinnedRowData) {
|
|
@@ -308,17 +283,28 @@ export function DataGrid({
|
|
|
308
283
|
return () => cancelAnimationFrame(id);
|
|
309
284
|
}, [pinnedRowData, tableData]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
310
285
|
|
|
311
|
-
// Compute cumulative sticky top for each pinned row
|
|
312
286
|
const pinnedRowTops = useMemo(() => {
|
|
313
287
|
const tops = new Map<number, number>();
|
|
314
288
|
let cumTop = headerHeight;
|
|
315
289
|
for (const { idx } of pinnedRowData) {
|
|
316
290
|
tops.set(idx, cumTop);
|
|
317
|
-
cumTop += pinnedRowHeights.get(idx) ?? 28;
|
|
291
|
+
cumTop += pinnedRowHeights.get(idx) ?? 28;
|
|
318
292
|
}
|
|
319
293
|
return tops;
|
|
320
294
|
}, [headerHeight, pinnedRowData, pinnedRowHeights]);
|
|
321
295
|
|
|
296
|
+
if (!tableData) {
|
|
297
|
+
return (
|
|
298
|
+
<div className="flex items-center justify-center h-full text-xs text-muted-foreground">
|
|
299
|
+
{loading ? <Loader2 className="size-4 animate-spin" /> : "Select a table"}
|
|
300
|
+
</div>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const totalPages = Math.ceil(tableData.total / tableData.limit) || 1;
|
|
305
|
+
const hasSelection = selectedRows.size > 0;
|
|
306
|
+
const allSelected = selectedRows.size === tableData.rows.length && tableData.rows.length > 0;
|
|
307
|
+
|
|
322
308
|
return (
|
|
323
309
|
<div ref={containerRef} tabIndex={0} className="flex flex-col h-full overflow-hidden outline-none">
|
|
324
310
|
{/* Search + bulk actions toolbar */}
|
|
@@ -391,7 +377,12 @@ export function DataGrid({
|
|
|
391
377
|
)}
|
|
392
378
|
|
|
393
379
|
{/* Table */}
|
|
394
|
-
<div className="flex-1 overflow-auto">
|
|
380
|
+
<div className="flex-1 overflow-auto relative">
|
|
381
|
+
{loading && (
|
|
382
|
+
<div className="absolute inset-0 z-30 flex items-center justify-center bg-background/60">
|
|
383
|
+
<Loader2 className="size-5 animate-spin text-primary" />
|
|
384
|
+
</div>
|
|
385
|
+
)}
|
|
395
386
|
<table className="w-full text-xs" style={{ borderCollapse: "separate", borderSpacing: 0 }}>
|
|
396
387
|
<thead ref={theadRef} className="sticky top-0 z-20 bg-muted">
|
|
397
388
|
<tr>
|
|
@@ -7,6 +7,17 @@ import { ExportButton } from "./export-button";
|
|
|
7
7
|
import { DataGrid } from "./data-grid";
|
|
8
8
|
import type { SchemaInfo } from "./sql-completion-provider";
|
|
9
9
|
|
|
10
|
+
/** Parse WHERE "col" ILIKE '%val%' clauses from SQL */
|
|
11
|
+
function parseSqlFilters(sql: string): Record<string, string> {
|
|
12
|
+
const filters: Record<string, string> = {};
|
|
13
|
+
const re = /"(\w+)"\s+ILIKE\s+'%([^']*?)%'/gi;
|
|
14
|
+
let m: RegExpExecArray | null;
|
|
15
|
+
while ((m = re.exec(sql)) !== null) {
|
|
16
|
+
filters[m[1]!] = m[2]!.replace(/''/g, "'");
|
|
17
|
+
}
|
|
18
|
+
return filters;
|
|
19
|
+
}
|
|
20
|
+
|
|
10
21
|
interface Props { metadata?: Record<string, unknown>; tabId?: string }
|
|
11
22
|
|
|
12
23
|
/** Generic database viewer — works for any DB type via unified API */
|
|
@@ -52,9 +63,8 @@ export function DatabaseViewer({ metadata }: Props) {
|
|
|
52
63
|
if (!db.selectedTable || Object.keys(columnFilters).length === 0) return;
|
|
53
64
|
clearTimeout(filterTimerRef.current);
|
|
54
65
|
filterTimerRef.current = setTimeout(() => {
|
|
55
|
-
// Execute
|
|
56
|
-
db.
|
|
57
|
-
setShowingQueryResult(true);
|
|
66
|
+
// Execute filter query into tableData — stays in table grid mode
|
|
67
|
+
db.queryAsTable(defaultQuery);
|
|
58
68
|
}, 500);
|
|
59
69
|
return () => clearTimeout(filterTimerRef.current);
|
|
60
70
|
}, [columnFilters]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
@@ -110,9 +120,21 @@ export function DatabaseViewer({ metadata }: Props) {
|
|
|
110
120
|
// Track whether user ran a custom query (show results instead of table grid)
|
|
111
121
|
const [showingQueryResult, setShowingQueryResult] = useState(!!initialSql);
|
|
112
122
|
const handleExecuteQuery = useCallback((sql: string) => {
|
|
123
|
+
const trimmed = sql.trim();
|
|
124
|
+
// Check if query is a simple SELECT on the current table — stay in table grid mode
|
|
125
|
+
if (db.selectedTable) {
|
|
126
|
+
const tablePattern = new RegExp(`^SELECT\\s+\\*\\s+FROM\\s+"?${db.selectedTable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}"?\\b`, "i");
|
|
127
|
+
if (tablePattern.test(trimmed)) {
|
|
128
|
+
// Parse ILIKE filters from SQL and sync to columnFilters
|
|
129
|
+
const parsed = parseSqlFilters(trimmed);
|
|
130
|
+
setColumnFilters(parsed);
|
|
131
|
+
db.queryAsTable(trimmed);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
113
135
|
setShowingQueryResult(true);
|
|
114
136
|
db.executeQuery(sql);
|
|
115
|
-
}, [db.executeQuery]);
|
|
137
|
+
}, [db.executeQuery, db.queryAsTable, db.selectedTable]);
|
|
116
138
|
|
|
117
139
|
// When user interacts with DataGrid (sort/page), switch back to table view
|
|
118
140
|
const handleToggleSort = useCallback((col: string) => {
|
|
@@ -173,7 +195,7 @@ export function DatabaseViewer({ metadata }: Props) {
|
|
|
173
195
|
orderBy={db.orderBy} orderDir={db.orderDir} onToggleSort={handleToggleSort}
|
|
174
196
|
onBulkDelete={db.bulkDelete} onInsertRow={db.insertRow}
|
|
175
197
|
connectionId={connectionId} selectedTable={db.selectedTable} selectedSchema={db.selectedSchema}
|
|
176
|
-
connectionName={connectionName} onColumnFilter={handleColumnFilter} />
|
|
198
|
+
connectionName={connectionName} columnFilters={columnFilters} onColumnFilter={handleColumnFilter} />
|
|
177
199
|
)}
|
|
178
200
|
|
|
179
201
|
{showQueryResults && (
|
|
@@ -173,12 +173,27 @@ export function useDatabase(connectionId: number) {
|
|
|
173
173
|
}
|
|
174
174
|
}, [base, selectedTable, selectedSchema, fetchTableData]);
|
|
175
175
|
|
|
176
|
+
/** Execute SQL but put results into tableData (for column filters) */
|
|
177
|
+
const queryAsTable = useCallback(async (sqlText: string) => {
|
|
178
|
+
setLoading(true);
|
|
179
|
+
try {
|
|
180
|
+
const result = await api.post<DbQueryResult>(`${base}/query`, { sql: sqlText });
|
|
181
|
+
if (result.changeType === "select") {
|
|
182
|
+
setTableData({ columns: result.columns, rows: result.rows, total: result.rows.length, page: 1, limit: result.rows.length });
|
|
183
|
+
}
|
|
184
|
+
} catch (e) {
|
|
185
|
+
setError((e as Error).message);
|
|
186
|
+
} finally {
|
|
187
|
+
setLoading(false);
|
|
188
|
+
}
|
|
189
|
+
}, [base]);
|
|
190
|
+
|
|
176
191
|
return {
|
|
177
192
|
selectedTable, selectedSchema, selectTable, tableData, schema,
|
|
178
193
|
loading, error, page, setPage: changePage,
|
|
179
194
|
orderBy, orderDir, toggleSort,
|
|
180
195
|
queryResult, queryError, queryLoading, executeQuery,
|
|
181
196
|
updateCell, deleteRow, bulkDelete, insertRow,
|
|
182
|
-
refreshData: fetchTableData,
|
|
197
|
+
refreshData: fetchTableData, queryAsTable,
|
|
183
198
|
};
|
|
184
199
|
}
|
|
@@ -86,6 +86,36 @@ export function CodeEditor({ metadata, tabId }: CodeEditorProps) {
|
|
|
86
86
|
|
|
87
87
|
const selectedSqlConn = useMemo(() => connections.find((c) => c.id === sqlConnId) ?? null, [connections, sqlConnId]);
|
|
88
88
|
|
|
89
|
+
// Beautify for inline content (must be before early returns to maintain hook order)
|
|
90
|
+
const canBeautifyInline = inlineContent != null && (inlineLanguage === "json" || inlineLanguage === "xml");
|
|
91
|
+
const [isBeautified, setIsBeautified] = useState(false);
|
|
92
|
+
const handleBeautifyInline = useCallback(() => {
|
|
93
|
+
if (!inlineContent) return;
|
|
94
|
+
if (isBeautified) {
|
|
95
|
+
setContent(inlineContent);
|
|
96
|
+
setIsBeautified(false);
|
|
97
|
+
} else {
|
|
98
|
+
const trimmed = inlineContent.trimStart();
|
|
99
|
+
if (inlineLanguage === "json") {
|
|
100
|
+
try { setContent(JSON.stringify(JSON.parse(trimmed), null, 2)); setIsBeautified(true); } catch { /* not valid */ }
|
|
101
|
+
} else if (inlineLanguage === "xml") {
|
|
102
|
+
let indent = 0;
|
|
103
|
+
const formatted = trimmed.replace(/(>)(<)(\/*)/g, "$1\n$2$3")
|
|
104
|
+
.split("\n")
|
|
105
|
+
.map((line) => {
|
|
106
|
+
const l = line.trim();
|
|
107
|
+
if (l.startsWith("</")) indent = Math.max(0, indent - 1);
|
|
108
|
+
const padded = " ".repeat(indent) + l;
|
|
109
|
+
if (l.startsWith("<") && !l.startsWith("</") && !l.endsWith("/>") && !l.includes("</")) indent++;
|
|
110
|
+
return padded;
|
|
111
|
+
})
|
|
112
|
+
.join("\n");
|
|
113
|
+
setContent(formatted);
|
|
114
|
+
setIsBeautified(true);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}, [inlineContent, inlineLanguage, isBeautified]);
|
|
118
|
+
|
|
89
119
|
// Persist selected connection per file
|
|
90
120
|
const handleSqlConnChange = useCallback((connId: number) => {
|
|
91
121
|
setSqlConnId(connId);
|
|
@@ -362,36 +392,6 @@ export function CodeEditor({ metadata, tabId }: CodeEditorProps) {
|
|
|
362
392
|
</div>
|
|
363
393
|
) : null;
|
|
364
394
|
|
|
365
|
-
// Beautify for inline content
|
|
366
|
-
const canBeautifyInline = inlineContent != null && (inlineLanguage === "json" || inlineLanguage === "xml");
|
|
367
|
-
const [isBeautified, setIsBeautified] = useState(false);
|
|
368
|
-
const handleBeautifyInline = useCallback(() => {
|
|
369
|
-
if (!inlineContent) return;
|
|
370
|
-
if (isBeautified) {
|
|
371
|
-
setContent(inlineContent);
|
|
372
|
-
setIsBeautified(false);
|
|
373
|
-
} else {
|
|
374
|
-
const trimmed = inlineContent.trimStart();
|
|
375
|
-
if (inlineLanguage === "json") {
|
|
376
|
-
try { setContent(JSON.stringify(JSON.parse(trimmed), null, 2)); setIsBeautified(true); } catch { /* not valid */ }
|
|
377
|
-
} else if (inlineLanguage === "xml") {
|
|
378
|
-
let indent = 0;
|
|
379
|
-
const formatted = trimmed.replace(/(>)(<)(\/*)/g, "$1\n$2$3")
|
|
380
|
-
.split("\n")
|
|
381
|
-
.map((line) => {
|
|
382
|
-
const l = line.trim();
|
|
383
|
-
if (l.startsWith("</")) indent = Math.max(0, indent - 1);
|
|
384
|
-
const padded = " ".repeat(indent) + l;
|
|
385
|
-
if (l.startsWith("<") && !l.startsWith("</") && !l.endsWith("/>") && !l.includes("</")) indent++;
|
|
386
|
-
return padded;
|
|
387
|
-
})
|
|
388
|
-
.join("\n");
|
|
389
|
-
setContent(formatted);
|
|
390
|
-
setIsBeautified(true);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
}, [inlineContent, inlineLanguage, isBeautified]);
|
|
394
|
-
|
|
395
395
|
return (
|
|
396
396
|
<div className="flex flex-col h-full w-full overflow-hidden">
|
|
397
397
|
{/* Inline content toolbar (cell viewer mode) */}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/csv-preview-D2pJJj3K.js","assets/chunk-CFjPhJqf.js","assets/lib-DurwGtQO.js","assets/react-nm2Ru1Pt.js","assets/createLucideIcon-PuMiQgHl.js","assets/arrow-up-BYhx9ckd.js","assets/react-dom-Bpkvzu3U.js","assets/jsx-runtime-kMwlnEGE.js","assets/csv-parser-CNNw2RVA.js"])))=>i.map(i=>d[i]);
|
|
2
|
-
import{o as e}from"./chunk-CFjPhJqf.js";import{t}from"./react-nm2Ru1Pt.js";import"./react-dom-Bpkvzu3U.js";import{t as n}from"./createLucideIcon-PuMiQgHl.js";import{t as r}from"./chevron-right-4zq1jPv6.js";import{n as i,t as a}from"./markdown-renderer-BUqab2os.js";import{t as o}from"./table-DFevCOMd.js";import{t as s}from"./text-wrap-BWNOVswA.js";import{t as c}from"./preload-helper-Bf_JiD2A.js";import{t as l}from"./jsx-runtime-kMwlnEGE.js";import"./dist-DIV6WgAG.js";import{t as u}from"./utils-BNytJOb1.js";import{i as d,r as f,t as p}from"./api-client-BfBM3I7n.js";import{Ct as m,Et as ee,K as h,R as g,S as _,St as v,Tt as y,Y as te,_ as b,_t as x,a as ne,at as re,b as S,bt as C,g as w,pt as T,v as E,vt as D,w as ie,wt as O,x as k,xt as A,y as j,yt as M}from"./index-DwrCg0TN.js";import"./chunk-GEFDOKGD-D-pKjlVd.js";import"./src-BqX54PbV.js";import"./chunk-7R4GIKGN-Dv-4cAYn.js";import"./chunk-HHEYEP7N-C7vxA5i9.js";import"./dist-CSJdAyA9.js";import"./chunk-PU5JKC2W-ek7k4QVB.js";import"./chunk-MX3YWQON-BpS_PtKp.js";import"./chunk-YBOYWFTD-rQG3QH5s.js";import"./chunk-PQ6SQG4A-TF58UVMU.js";import"./chunk-KYZI473N-Bb0MCaIO.js";import"./chunk-O4XLMI2P-nDhi_cVu.js";import"./chunk-GLR3WWYH-DKikpoJM.js";import"./chunk-XPW4576I-BPQQBakK.js";import{n as ae,t as oe}from"./use-monaco-theme-U9ZhfvHB.js";import{n as se,t as ce}from"./sql-completion-provider-DM9Qov6L.js";var N=n(`file-exclamation-point`,[[`path`,{d:`M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z`,key:`1oefj6`}],[`path`,{d:`M12 9v4`,key:`juzpu7`}],[`path`,{d:`M12 17h.01`,key:`p32p05`}]]),P=e(t(),1),F=l(),le={ts:A,tsx:A,js:A,jsx:A,py:A,rs:A,go:A,html:A,css:A,scss:A,json:v,md:C,txt:C,yaml:M,yml:M};function ue(e,t){return t?x:le[e.split(`.`).pop()?.toLowerCase()??``]??D}function I(e,t){let n=[],r=e;for(let e=0;e<t.length;e++){let i=t[e],a=t.slice(0,e+1).join(`/`),o=r.find(e=>e.name===i);if(n.push({name:i,fullPath:a,node:o??null,siblings:r}),o?.children)r=o.children;else{for(let r=e+1;r<t.length;r++)n.push({name:t[r],fullPath:t.slice(0,r+1).join(`/`),node:null,siblings:[]});break}}return n}function L(e){return[...e].sort((e,t)=>e.type===t.type?e.name.localeCompare(t.name):e.type===`directory`?-1:1)}function de({filePath:e,projectName:t,tabId:n,className:i}){let a=g(e=>e.tree),{updateTab:o,openTab:s}=h(),c=(0,P.useRef)(null),l=(0,P.useMemo)(()=>I(a,e.split(`/`).filter(Boolean)),[a,e]);(0,P.useEffect)(()=>{c.current&&(c.current.scrollLeft=c.current.scrollWidth)},[l]);function d(e,r){let i=u(e);r.metaKey||r.ctrlKey?s({type:`editor`,title:i,metadata:{filePath:e,projectName:t},projectId:t,closable:!0}):o(n,{title:i,metadata:{filePath:e,projectName:t}})}return(0,F.jsx)(`div`,{ref:c,className:i,children:l.map((e,n)=>(0,F.jsxs)(`div`,{className:`flex items-center shrink-0`,children:[n>0&&(0,F.jsx)(r,{className:`size-3 text-muted-foreground shrink-0 mx-0.5`}),e.siblings.length>0?(0,F.jsx)(fe,{segment:e,isLast:n===l.length-1,projectName:t,onFileClick:d}):(0,F.jsx)(`span`,{className:`text-xs text-muted-foreground px-1 py-0.5`,children:e.name})]},e.fullPath))})}function fe({segment:e,isLast:t,projectName:n,onFileClick:r}){let i=(0,P.useMemo)(()=>L(e.siblings),[e.siblings]);return(0,F.jsxs)(w,{children:[(0,F.jsx)(_,{asChild:!0,children:(0,F.jsx)(`button`,{type:`button`,className:`text-xs px-1 py-0.5 rounded hover:bg-muted transition-colors truncate max-w-[120px] ${t?`text-foreground font-medium`:`text-muted-foreground`}`,children:e.name})}),(0,F.jsx)(b,{align:`start`,className:`max-h-[300px] p-1`,children:i.map(t=>(0,F.jsx)(R,{node:t,projectName:n,activePath:e.fullPath,onFileClick:r},t.path))})]})}function R({node:e,projectName:t,activePath:n,onFileClick:r}){let i=ue(e.name,e.type===`directory`),a=e.path===n;return e.type===`directory`&&e.children&&e.children.length>0?(0,F.jsxs)(j,{children:[(0,F.jsxs)(k,{className:`text-xs gap-1.5 ${a?`bg-muted`:``}`,children:[(0,F.jsx)(i,{className:`size-3.5 shrink-0 text-muted-foreground`}),(0,F.jsx)(`span`,{className:`truncate`,children:e.name})]}),(0,F.jsx)(S,{className:`max-h-[300px] overflow-y-auto p-1`,children:L(e.children).map(e=>(0,F.jsx)(R,{node:e,projectName:t,activePath:n,onFileClick:r},e.path))})]}):(0,F.jsxs)(E,{className:`text-xs gap-1.5 cursor-pointer ${a?`bg-muted`:``}`,onSelect:e=>{},onClick:t=>{e.type!==`directory`&&r(e.path,t)},children:[(0,F.jsx)(i,{className:`size-3.5 shrink-0 text-muted-foreground`}),(0,F.jsx)(`span`,{className:`truncate`,children:e.name})]})}function z({active:e,onClick:t,icon:n,label:r}){return(0,F.jsxs)(`button`,{type:`button`,onClick:t,className:`flex items-center gap-1 px-2 py-1 rounded text-xs transition-colors ${e?`bg-muted text-foreground`:`text-muted-foreground hover:text-foreground`}`,children:[(0,F.jsx)(n,{className:`size-3`}),(0,F.jsx)(`span`,{className:`hidden sm:inline`,children:r})]})}function pe({ext:e,mdMode:t,onMdModeChange:n,csvMode:r,onCsvModeChange:a,wordWrap:c,onToggleWordWrap:l,filePath:u,projectName:d,className:f}){return(0,F.jsxs)(`div`,{className:f,children:[(e===`md`||e===`mdx`)&&n&&(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(z,{active:t===`edit`,onClick:()=>n(`edit`),icon:i,label:`Edit`}),(0,F.jsx)(z,{active:t===`preview`,onClick:()=>n(`preview`),icon:m,label:`Preview`})]}),e===`csv`&&a&&(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(z,{active:r===`table`,onClick:()=>a(`table`),icon:o,label:`Table`}),(0,F.jsx)(z,{active:r===`raw`,onClick:()=>a(`raw`),icon:i,label:`Raw`})]}),(0,F.jsx)(z,{active:c,onClick:l,icon:s,label:`Wrap`}),u&&d&&(0,F.jsx)(z,{active:!1,onClick:()=>ie(d,u),icon:y,label:`Download`})]})}var me=(0,P.lazy)(()=>c(()=>import(`./csv-preview-D2pJJj3K.js`).then(e=>({default:e.CsvPreview})),__vite__mapDeps([0,1,2,3,4,5,6,7,8]))),he=new Set([`png`,`jpg`,`jpeg`,`gif`,`webp`,`svg`,`ico`]),ge=new Set([`db`,`sqlite`,`sqlite3`]);function B(e){return e.split(`.`).pop()?.toLowerCase()??``}function _e(e){return{js:`javascript`,jsx:`javascript`,ts:`typescript`,tsx:`typescript`,py:`python`,html:`html`,css:`css`,scss:`scss`,json:`json`,md:`markdown`,mdx:`markdown`,yaml:`yaml`,yml:`yaml`,sh:`shell`,bash:`shell`,sql:`sql`}[B(e)]??`plaintext`}function V({metadata:e,tabId:t}){let n=e?.filePath,r=e?.projectName,i=e?.inlineContent,a=e?.inlineLanguage,[o,s]=(0,P.useState)(i??null),[c,l]=(0,P.useState)(`utf-8`),[f,m]=(0,P.useState)(!0),[g,_]=(0,P.useState)(null),[v,y]=(0,P.useState)(!1),b=(0,P.useRef)(null),x=(0,P.useRef)(``),S=(0,P.useRef)(null),{tabs:C,updateTab:w}=h(),{wordWrap:E,toggleWordWrap:D}=te(),ie=oe(),O=C.find(e=>e.id===t),k=n?B(n):``,A=he.has(k),j=k===`pdf`,M=ge.has(k),le=k===`md`||k===`mdx`,ue=k===`csv`,I=k===`sql`,[L,fe]=(0,P.useState)(`preview`),[R,z]=(0,P.useState)(`table`),{connections:V,cachedTables:xe,refreshTables:Se}=ne(),[H,Ce]=(0,P.useState)(()=>{if(!I||!n)return null;let e=localStorage.getItem(`ppm:sql-conn:${n}`);return e?Number(e):null}),U=(0,P.useRef)(null),W=(0,P.useRef)(null),G=(0,P.useMemo)(()=>V.find(e=>e.id===H)??null,[V,H]),we=(0,P.useCallback)(e=>{Ce(e),n&&localStorage.setItem(`ppm:sql-conn:${n}`,String(e)),Se(e).catch(()=>{})},[n,Se]),K=(0,P.useMemo)(()=>{if(!I||!H)return;let e=(xe.get(H)??[]).map(e=>({name:e.tableName,schema:e.schemaName}));if(e.length!==0)return{tables:e,getColumns:async(e,t)=>p.get(`/api/db/connections/${H}/schema?table=${encodeURIComponent(e)}${t?`&schema=${encodeURIComponent(t)}`:``}`)}},[I,H,xe]);(0,P.useEffect)(()=>{if(!(!U.current||!K))return W.current?.dispose(),ce(),W.current=U.current.languages.registerCompletionItemProvider(`sql`,se(U.current,K)),()=>{W.current?.dispose()}},[K]);let Te=h(e=>e.openTab),q=(0,P.useCallback)(e=>{G&&Te({type:`database`,title:`${G.name} · Query`,projectId:null,closable:!0,metadata:{connectionId:G.id,connectionName:G.name,dbType:G.type,initialSql:e}})},[G,Te]),Ee=(0,P.useCallback)(()=>{if(!S.current||!G)return;let e=S.current,t=e.getSelection();q(t&&!t.isEmpty()?e.getModel()?.getValueInRange(t)??e.getValue():e.getValue())},[G,q]),J=(0,P.useRef)([]),Y=(0,P.useRef)(q);Y.current=q,(0,P.useEffect)(()=>{M&&t&&w(t,{type:`sqlite`})},[M,t,w]);let X=n?/^(\/|[A-Za-z]:[/\\])/.test(n):!1;(0,P.useEffect)(()=>{if(i!=null){m(!1);return}if(!n||!X&&!r)return;if(A||j){m(!1);return}m(!0),_(null);let e=X?`/api/fs/read?path=${encodeURIComponent(n)}`:`${d(r)}/files/read?path=${encodeURIComponent(n)}`;return p.get(e).then(e=>{s(e.content),e.encoding&&l(e.encoding),x.current=e.content,m(!1)}).catch(e=>{_(e instanceof Error?e.message:`Failed to load file`),m(!1)}),()=>{b.current&&clearTimeout(b.current)}},[n,r,A,j,X]),(0,P.useEffect)(()=>{if(!O)return;let e=n?u(n):`Untitled`,t=v?`${e} \u25CF`:e;O.title!==t&&w(O.id,{title:t})},[v]);let De=(0,P.useCallback)(async e=>{if(n&&!(!X&&!r))try{X?await p.put(`/api/fs/write`,{path:n,content:e}):await p.put(`${d(r)}/files/write`,{path:n,content:e}),y(!1)}catch{}},[n,r,X]);function Oe(e){let t=e??``;s(t),x.current=t,y(!0),b.current&&clearTimeout(b.current),b.current=setTimeout(()=>De(x.current),1e3)}let Z=e?.lineNumber,ke=(0,P.useCallback)((e,t)=>{if(S.current=e,U.current=t,Z&&Z>0&&setTimeout(()=>{e.revealLineInCenter(Z),e.setPosition({lineNumber:Z,column:1}),e.focus()},100),e.addCommand(t.KeyMod.Alt|t.KeyCode.KeyZ,()=>te.getState().toggleWordWrap()),t.languages.typescript.typescriptDefaults.setDiagnosticsOptions({noSemanticValidation:!0,noSyntaxValidation:!0,noSuggestionDiagnostics:!0}),t.languages.typescript.javascriptDefaults.setDiagnosticsOptions({noSemanticValidation:!0,noSyntaxValidation:!0,noSuggestionDiagnostics:!0}),K&&(W.current?.dispose(),W.current=t.languages.registerCompletionItemProvider(`sql`,se(t,K))),I){J.current.forEach(e=>e.dispose()),J.current=[];let n=e.addCommand(0,(e,t)=>{t&&Y.current(t)});if(n){let e=t.languages.registerCodeLensProvider(`sql`,{provideCodeLenses:e=>{let t=[],r=e.getValue().split(`
|
|
3
|
-
`),i=-1,a=[],o=(e,r)=>{let i=r.trim();!i||i.startsWith(`--`)||t.push({range:{startLineNumber:e,startColumn:1,endLineNumber:e,endColumn:1},command:{id:n,title:`▷ Run`,arguments:[i]}})};for(let e=0;e<r.length;e++){let t=r[e].trim();if(i===-1){if(!t||t.startsWith(`--`))continue;i=e+1,a=[]}a.push(r[e]),t.endsWith(`;`)&&(o(i,a.join(`
|
|
4
|
-
`)),i=-1,a=[])}return i>0&&a.join(``).trim()&&o(i,a.join(`
|
|
5
|
-
`)),{lenses:t,dispose:()=>{}}}});J.current.push(e)}}},[K]);if(!i&&(!n||!X&&!r))return(0,F.jsx)(`div`,{className:`flex items-center justify-center h-full text-text-secondary text-sm`,children:`No file selected.`});if(f)return(0,F.jsxs)(`div`,{className:`flex items-center justify-center h-full gap-2 text-text-secondary`,children:[(0,F.jsx)(T,{className:`size-5 animate-spin`}),(0,F.jsx)(`span`,{className:`text-sm`,children:`Loading file...`})]});if(g)return(0,F.jsx)(`div`,{className:`flex items-center justify-center h-full text-error text-sm`,children:g});if(A)return(0,F.jsx)(ye,{filePath:n,projectName:r});if(j)return(0,F.jsx)(be,{filePath:n,projectName:r});if(c===`base64`)return(0,F.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,F.jsx)(N,{className:`size-10 text-text-subtle`}),(0,F.jsx)(`p`,{className:`text-sm`,children:`This file is a binary format and cannot be displayed.`}),(0,F.jsx)(`p`,{className:`text-xs text-text-subtle`,children:n})]});let Ae=I?(0,F.jsxs)(`div`,{className:`shrink-0 flex items-center gap-1 px-2 border-l border-border`,children:[(0,F.jsx)(ee,{className:`size-3 text-muted-foreground`}),(0,F.jsxs)(`select`,{value:H??``,onChange:e=>{let t=Number(e.target.value);t&&we(t)},className:`h-5 text-[10px] bg-transparent border border-border rounded px-1 text-foreground outline-none max-w-[140px]`,title:`Select connection for autocomplete`,children:[(0,F.jsx)(`option`,{value:``,children:`Connection…`}),V.map(e=>(0,F.jsx)(`option`,{value:e.id,children:e.name},e.id))]}),(0,F.jsx)(`button`,{type:`button`,onClick:Ee,disabled:!G,className:`p-0.5 rounded text-muted-foreground hover:text-primary disabled:opacity-30 transition-colors`,title:`Run all in DB Viewer`,children:(0,F.jsx)(re,{className:`size-3.5`})})]}):null,je=i!=null&&(a===`json`||a===`xml`),[Q,$]=(0,P.useState)(!1),Me=(0,P.useCallback)(()=>{if(i)if(Q)s(i),$(!1);else{let e=i.trimStart();if(a===`json`)try{s(JSON.stringify(JSON.parse(e),null,2)),$(!0)}catch{}else if(a===`xml`){let t=0;s(e.replace(/(>)(<)(\/*)/g,`$1
|
|
6
|
-
$2$3`).split(`
|
|
7
|
-
`).map(e=>{let n=e.trim();n.startsWith(`</`)&&(t=Math.max(0,t-1));let r=` `.repeat(t)+n;return n.startsWith(`<`)&&!n.startsWith(`</`)&&!n.endsWith(`/>`)&&!n.includes(`</`)&&t++,r}).join(`
|
|
8
|
-
`)),$(!0)}}},[i,a,Q]);return(0,F.jsxs)(`div`,{className:`flex flex-col h-full w-full overflow-hidden`,children:[i!=null&&je&&(0,F.jsx)(`div`,{className:`flex items-center h-7 border-b border-border bg-background shrink-0 px-2 gap-2`,children:(0,F.jsx)(`button`,{type:`button`,onClick:Me,className:`text-[10px] px-2 py-0.5 rounded border border-border hover:bg-muted transition-colors text-foreground`,children:Q?`Raw`:`Beautify`})}),n&&r&&t&&(0,F.jsxs)(`div`,{className:`hidden md:flex items-center h-7 border-b border-border bg-background shrink-0`,children:[(0,F.jsx)(de,{filePath:n,projectName:r,tabId:t,className:`flex items-center flex-1 min-w-0 overflow-x-auto scrollbar-none px-2 gap-0.5`}),Ae,(0,F.jsx)(pe,{ext:k,mdMode:L,onMdModeChange:fe,csvMode:R,onCsvModeChange:z,wordWrap:E,onToggleWordWrap:D,filePath:n,projectName:r,className:`shrink-0 flex items-center gap-1 px-2`})]}),I&&(!r||!t)&&(0,F.jsxs)(`div`,{className:`hidden md:flex items-center h-7 border-b border-border bg-background shrink-0 px-2`,children:[(0,F.jsx)(`span`,{className:`text-xs text-muted-foreground truncate flex-1`,children:n?u(n):`SQL`}),Ae]}),ue&&R===`table`?(0,F.jsx)(P.Suspense,{fallback:(0,F.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,F.jsx)(T,{className:`size-5 animate-spin text-text-subtle`})}),children:(0,F.jsx)(me,{content:o??``,onContentChange:Oe,wordWrap:E})}):le&&L===`preview`?(0,F.jsx)(ve,{content:o??``}):(0,F.jsx)(`div`,{className:`flex-1 overflow-hidden`,children:(0,F.jsx)(ae,{height:`100%`,language:a??_e(n??``),value:o??``,onChange:i==null?Oe:void 0,onMount:ke,theme:ie,options:{fontSize:13,fontFamily:`Menlo, Monaco, Consolas, monospace`,wordWrap:E?`on`:`off`,minimap:{enabled:!1},scrollBeyondLastLine:!1,automaticLayout:!0,lineNumbers:`on`,folding:!0,bracketPairColorization:{enabled:!0},readOnly:i!=null},loading:(0,F.jsx)(T,{className:`size-5 animate-spin text-text-subtle`})})})]})}function ve({content:e}){return(0,F.jsx)(a,{content:e,className:`flex-1 overflow-auto p-4`})}function ye({filePath:e,projectName:t}){let[n,r]=(0,P.useState)(null),[i,a]=(0,P.useState)(!1);return(0,P.useEffect)(()=>{let n,i=`${d(t)}/files/raw?path=${encodeURIComponent(e)}`,o=f();return fetch(i,{headers:o?{Authorization:`Bearer ${o}`}:{}}).then(e=>{if(!e.ok)throw Error(`Failed`);return e.blob()}).then(e=>{let t=URL.createObjectURL(e);n=t,r(t)}).catch(()=>a(!0)),()=>{n&&URL.revokeObjectURL(n)}},[e,t]),i?(0,F.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,F.jsx)(N,{className:`size-10 text-text-subtle`}),(0,F.jsx)(`p`,{className:`text-sm`,children:`Failed to load image.`})]}):n?(0,F.jsx)(`div`,{className:`flex items-center justify-center h-full p-4 bg-surface overflow-auto`,children:(0,F.jsx)(`img`,{src:n,alt:e,className:`max-w-full max-h-full object-contain`})}):(0,F.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,F.jsx)(T,{className:`size-5 animate-spin text-text-subtle`})})}function be({filePath:e,projectName:t}){let[n,r]=(0,P.useState)(null),[i,a]=(0,P.useState)(!1);(0,P.useEffect)(()=>{let n,i=`${d(t)}/files/raw?path=${encodeURIComponent(e)}`,o=f();return fetch(i,{headers:o?{Authorization:`Bearer ${o}`}:{}}).then(e=>{if(!e.ok)throw Error(`Failed`);return e.blob()}).then(e=>{let t=URL.createObjectURL(new Blob([e],{type:`application/pdf`}));n=t,r(t)}).catch(()=>a(!0)),()=>{n&&URL.revokeObjectURL(n)}},[e,t]);let o=(0,P.useCallback)(()=>{n&&window.open(n,`_blank`)},[n]);return i?(0,F.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,F.jsx)(N,{className:`size-10 text-text-subtle`}),(0,F.jsx)(`p`,{className:`text-sm`,children:`Failed to load PDF.`})]}):n?(0,F.jsxs)(`div`,{className:`flex flex-col h-full`,children:[(0,F.jsxs)(`div`,{className:`flex items-center justify-between px-3 py-1.5 border-b border-border bg-background shrink-0`,children:[(0,F.jsx)(`span`,{className:`text-xs text-text-secondary truncate`,children:e}),(0,F.jsxs)(`button`,{onClick:o,className:`flex items-center gap-1 text-xs text-text-secondary hover:text-text-primary transition-colors`,children:[(0,F.jsx)(O,{className:`size-3`}),` Open in new tab`]})]}),(0,F.jsx)(`iframe`,{src:n,title:e,className:`flex-1 w-full border-none`})]}):(0,F.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,F.jsx)(T,{className:`size-5 animate-spin text-text-subtle`})})}export{V as CodeEditor};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{o as e}from"./chunk-CFjPhJqf.js";import{t}from"./react-nm2Ru1Pt.js";import{t as n}from"./createLucideIcon-PuMiQgHl.js";import{n as r,t as i}from"./x-D2_KzIET.js";import{t as a}from"./chevron-right-4zq1jPv6.js";import{t as o}from"./jsx-runtime-kMwlnEGE.js";import{t as s}from"./api-client-BfBM3I7n.js";import{Ct as c,Et as l,K as u,Mt as d,Nt as f,Q as p,Tt as m,it as h,mt as g,ot as _,pt as v,rt as y,st as b,tt as x}from"./index-DwrCg0TN.js";import"./use-monaco-theme-U9ZhfvHB.js";import{t as S}from"./sql-query-editor-OhZa4Z9F.js";import{n as C}from"./csv-parser-CNNw2RVA.js";var w=n(`funnel`,[[`path`,{d:`M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z`,key:`sc7q7i`}]]),T=e(t(),1);function E(e,t,n,r){return`ppm-db-${e}-${n}.${t}-p${r}`}function D(e,t,n,r){try{let i=sessionStorage.getItem(E(e,t,n,r));return i?JSON.parse(i):null}catch{return null}}function ee(e,t,n,r,i,a){try{sessionStorage.setItem(E(e,t,n,r),JSON.stringify({data:i,cols:a}))}catch{}}function O(e){let t=`/api/db/connections/${e}`,[n,r]=(0,T.useState)(null),[i,a]=(0,T.useState)(`public`),[o,c]=(0,T.useState)(null),[l,u]=(0,T.useState)([]),[d,f]=(0,T.useState)(!1),[p,m]=(0,T.useState)(null),[h,g]=(0,T.useState)(1),[_,v]=(0,T.useState)(null),[y,b]=(0,T.useState)(null),[x,S]=(0,T.useState)(!1),[C,w]=(0,T.useState)(null),[E,O]=(0,T.useState)(`ASC`),k=(0,T.useCallback)(async(r,a,o,l,d)=>{let p=r??n,g=a??i;if(!p)return;f(!0);let _=l===void 0?C:l,v=d??E;try{let n=_?`&orderBy=${encodeURIComponent(_)}&orderDir=${v}`:``,[r,i]=await Promise.all([s.get(`${t}/data?table=${encodeURIComponent(p)}&schema=${g}&page=${o??h}&limit=100${n}`),s.get(`${t}/schema?table=${encodeURIComponent(p)}&schema=${g}`)]);c(r),u(i),ee(e,p,g,o??h,r,i)}catch(e){m(e.message)}finally{f(!1)}},[t,e,n,i,h,C,E]),A=(0,T.useCallback)((t,n=`public`)=>{r(t),a(n),g(1),v(null);let i=D(e,t,n,1);i?(c(i.data),u(i.cols),f(!1),k(t,n,1)):k(t,n,1)},[e,k]),j=(0,T.useCallback)(e=>{g(e),k(void 0,void 0,e)},[k]),M=(0,T.useCallback)(async e=>{S(!0),b(null);try{let r=await s.post(`${t}/query`,{sql:e});v(r),r.changeType===`modify`&&k(n??void 0,i)}catch(e){b(e.message)}finally{S(!1)}},[t,n,i,k]),N=(0,T.useCallback)(async(e,r,a,o)=>{if(!n)return;let c=n,l=i;try{await s.put(`${t}/cell`,{table:c,schema:l,pkColumn:e,pkValue:r,column:a,value:o}),k(c,l)}catch(e){m(e.message)}},[t,n,i,k]),P=(0,T.useCallback)(async(e,r)=>{if(!n)return;let a=n,o=i;try{await s.del(`${t}/row`,{table:a,schema:o,pkColumn:e,pkValue:r}),k(a,o)}catch(e){m(e.message)}},[t,n,i,k]);return{selectedTable:n,selectedSchema:i,selectTable:A,tableData:o,schema:l,loading:d,error:p,page:h,setPage:j,orderBy:C,orderDir:E,toggleSort:(0,T.useCallback)(e=>{let t,n=`ASC`;C===e?E===`ASC`?(t=e,n=`DESC`):(t=null,n=`ASC`):(t=e,n=`ASC`),w(t),O(n),g(1),k(void 0,void 0,1,t,n)},[C,E,k]),queryResult:_,queryError:y,queryLoading:x,executeQuery:M,updateCell:N,deleteRow:P,bulkDelete:(0,T.useCallback)(async(e,r)=>{if(!n)return;let a=n,o=i;try{await s.post(`${t}/rows/delete`,{table:a,schema:o,pkColumn:e,pkValues:r}),k(a,o)}catch(e){m(e.message)}},[t,n,i,k]),insertRow:(0,T.useCallback)(async e=>{if(!n)return;let r=n,a=i;try{await s.post(`${t}/row`,{table:r,schema:a,values:e}),k(r,a)}catch(e){throw m(e.message),e}},[t,n,i,k]),refreshData:k}}var k=o();function A(e,t,n){let r=new Blob([t],{type:n}),i=URL.createObjectURL(r),a=document.createElement(`a`);a.href=i,a.download=e,a.click(),URL.revokeObjectURL(i)}function j({columns:e,rows:t,filename:n=`export`,exportAllUrl:r}){let[i,a]=(0,T.useState)(!1),[o,s]=(0,T.useState)(!1),c=(0,T.useRef)(null);(0,T.useEffect)(()=>{if(!i)return;let e=e=>{c.current&&!c.current.contains(e.target)&&a(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[i]);let l=()=>{let r=C(e,t.map(t=>e.map(e=>String(t[e]??``))));A(`${n}.csv`,r,`text/csv`),a(!1)},u=()=>{let e=JSON.stringify(t,null,2);A(`${n}.json`,e,`application/json`),a(!1)},d=async e=>{if(r){s(!0);try{let t=await(await fetch(`${r}&format=${e}&limit=10000`)).text(),i=e===`csv`?`text/csv`:`application/json`;A(`${n}-all.${e}`,t,i)}catch{}s(!1),a(!1)}};return e.length===0||t.length===0?null:(0,k.jsxs)(`div`,{className:`relative`,ref:c,children:[(0,k.jsx)(`button`,{type:`button`,onClick:()=>a(e=>!e),className:`p-1 rounded text-muted-foreground hover:text-foreground transition-colors`,title:`Export`,children:(0,k.jsx)(m,{className:`size-3.5`})}),i&&(0,k.jsxs)(`div`,{className:`absolute right-0 top-full mt-1 z-50 bg-popover border border-border rounded-md shadow-md py-1 min-w-[160px] text-xs`,children:[(0,k.jsx)(`button`,{onClick:l,className:`w-full text-left px-3 py-1.5 hover:bg-muted transition-colors`,children:`Export Page (CSV)`}),(0,k.jsx)(`button`,{onClick:u,className:`w-full text-left px-3 py-1.5 hover:bg-muted transition-colors`,children:`Export Page (JSON)`}),r&&(0,k.jsxs)(k.Fragment,{children:[(0,k.jsx)(`div`,{className:`border-t border-border my-1`}),(0,k.jsx)(`button`,{onClick:()=>d(`csv`),disabled:o,className:`w-full text-left px-3 py-1.5 hover:bg-muted transition-colors disabled:opacity-50`,children:o?`Exporting…`:`Export All (CSV)`}),(0,k.jsx)(`button`,{onClick:()=>d(`json`),disabled:o,className:`w-full text-left px-3 py-1.5 hover:bg-muted transition-colors disabled:opacity-50`,children:o?`Exporting…`:`Export All (JSON)`})]})]})]})}function M({tableData:e,schema:t,loading:n,page:o,onPageChange:s,onCellUpdate:c,onRowDelete:l,orderBy:m,orderDir:g,onToggleSort:y,onBulkDelete:S,onInsertRow:C,connectionId:E,selectedTable:D,selectedSchema:ee,connectionName:O,onColumnFilter:A}){let[M,N]=(0,T.useState)(null),[P,F]=(0,T.useState)(``),[I,L]=(0,T.useState)(null),[R,re]=(0,T.useState)(``),[z,B]=(0,T.useState)(new Set),[ie,ae]=(0,T.useState)(!1),[oe,V]=(0,T.useState)({}),[se,H]=(0,T.useState)(null),[ce,U]=(0,T.useState)(!1),{openTab:le}=u(),ue=(0,T.useCallback)(e=>{le({type:`editor`,title:e.col,projectId:null,closable:!0,metadata:{inlineContent:e.value,inlineLanguage:te(e.value)}})},[le]),[W,de]=(0,T.useState)(new Set),[G,fe]=(0,T.useState)(new Set),[K,pe]=(0,T.useState)({}),[me,he]=(0,T.useState)(null),q=(0,T.useMemo)(()=>t.find(e=>e.pk)?.name||(t.find(e=>e.name.toLowerCase()===`id`)?.name??null),[t]),ge=(0,T.useRef)(M);ge.current=M;let J=(0,T.useRef)(P);J.current=P;let _e=(0,T.useRef)(z);_e.current=z;let ve=(0,T.useRef)(I);ve.current=I;let ye=(0,T.useCallback)((e,t,n)=>{N({rowIdx:e,col:t}),F(n==null?``:typeof n==`object`?JSON.stringify(n):String(n))},[]),be=(0,T.useCallback)(()=>{let t=ge.current;if(!t||!e||!q)return;let n=e.rows[t.rowIdx];if(!n)return;let r=n[t.col];String(r??``)!==J.current&&c(q,n[q],t.col,J.current===``?null:J.current),N(null)},[e,q,c]),xe=(0,T.useCallback)(()=>N(null),[]),Se=(0,T.useCallback)(t=>{if(!e||!q||!l)return;let n=e.rows[t];n&&(l(q,n[q]),L(null))},[e,q,l]),Ce=(0,T.useCallback)(e=>{de(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),we=(0,T.useCallback)(e=>{fe(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),Te=(0,T.useCallback)((e,t)=>{pe(n=>{let r={...n};return t?r[e]=t:delete r[e],r})},[]),Ee=(0,T.useRef)(K);(0,T.useEffect)(()=>{Ee.current!==K&&(Ee.current=K,A?.(K))},[K,A]);let De=(0,T.useCallback)(e=>{B(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),Oe=(0,T.useCallback)(()=>{e&&B(t=>t.size===e.rows.length?new Set:new Set(e.rows.map((e,t)=>t)))},[e]),ke=(0,T.useCallback)(()=>{!e||!q||!S||(S(q,Array.from(z).map(t=>e.rows[t]?.[q]).filter(e=>e!=null)),B(new Set),U(!1))},[e,q,S,z]),Ae=(0,T.useCallback)(async()=>{if(C){H(null);try{let e={};for(let[t,n]of Object.entries(oe))n!==``&&(e[t]=n);await C(e),ae(!1),V({})}catch(e){H(e.message)}}},[C,oe]),Y=(0,T.useMemo)(()=>{if(!e||!R)return e?.rows??[];let t=R.toLowerCase();return e.rows.filter(n=>e.columns.some(e=>String(n[e]??``).toLowerCase().includes(t)))},[e,R]),je=(0,T.useRef)(null);if((0,T.useEffect)(()=>{let t=je.current;if(!t)return;let n=t=>{if(!(t.metaKey||t.ctrlKey)||!e)return;let n=t.target?.tagName;if(!(n===`INPUT`||n===`TEXTAREA`)&&(t.key===`a`&&(t.preventDefault(),B(new Set(e.rows.map((e,t)=>t)))),t.key===`c`&&z.size>0)){t.preventDefault();let n=e.columns,r=n.join(` `),i=Array.from(z).sort((e,t)=>e-t).map(t=>n.map(n=>{let r=e.rows[t]?.[n];return r==null?``:typeof r==`object`?JSON.stringify(r):String(r)}).join(` `));navigator.clipboard.writeText([r,...i].join(`
|
|
2
|
-
`))}};return t.addEventListener(`keydown`,n),()=>t.removeEventListener(`keydown`,n)},[e,z]),!e)return(0,k.jsx)(`div`,{className:`flex items-center justify-center h-full text-xs text-muted-foreground`,children:n?(0,k.jsx)(v,{className:`size-4 animate-spin`}):`Select a table`});let Me=Math.ceil(e.total/e.limit)||1,Ne=z.size>0,Pe=z.size===e.rows.length&&e.rows.length>0,X=(0,T.useMemo)(()=>{let t=e.columns.filter(e=>W.has(e)),n=e.columns.filter(e=>!W.has(e));return[...t,...n]},[e.columns,W]),Fe=(0,T.useRef)(null),[Z,Ie]=(0,T.useState)(0),[Q,Le]=(0,T.useState)(new Map);(0,T.useEffect)(()=>{let e=Fe.current;if(!e)return;let t=()=>{Ie(e.offsetHeight);let t=new Map;e.querySelectorAll(`th[data-col]`).forEach(e=>{t.set(e.dataset.col,e.offsetWidth)});let n=e.querySelector(`th[data-col='_cb']`);n&&t.set(`_cb`,n.offsetWidth),Le(t)};t();let n=new ResizeObserver(t);return n.observe(e),()=>n.disconnect()},[e?.columns,W]);let Re=(0,T.useMemo)(()=>{let e=new Map,t=Q.get(`_cb`)??(q?40:0);for(let n of X){if(!W.has(n))break;e.set(n,t),t+=Q.get(n)??100}return e},[X,W,q,Q]),$=(0,T.useMemo)(()=>Array.from(G).sort((e,t)=>e-t).map(e=>({idx:e,row:Y[e]})).filter(e=>e.row),[G,Y]),ze=(0,T.useRef)(new Map),[Be,Ve]=(0,T.useState)(new Map),He=(0,T.useCallback)((e,t)=>{t?ze.current.set(e,t):ze.current.delete(e)},[]);(0,T.useEffect)(()=>{if($.length===0){Be.size>0&&Ve(new Map);return}let e=requestAnimationFrame(()=>{let e=new Map;for(let{idx:t}of $){let n=ze.current.get(t);n&&e.set(t,n.offsetHeight)}Ve(e)});return()=>cancelAnimationFrame(e)},[$,e]);let Ue=(0,T.useMemo)(()=>{let e=new Map,t=Z;for(let{idx:n}of $)e.set(n,t),t+=Be.get(n)??28;return e},[Z,$,Be]);return(0,k.jsxs)(`div`,{ref:je,tabIndex:0,className:`flex flex-col h-full overflow-hidden outline-none`,children:[(0,k.jsxs)(`div`,{className:`flex items-center gap-2 px-2 py-1 border-b border-border bg-background shrink-0`,children:[(0,k.jsxs)(`div`,{className:`flex items-center gap-1 flex-1`,children:[(0,k.jsx)(x,{className:`size-3 text-muted-foreground`}),(0,k.jsx)(`input`,{type:`text`,value:R,onChange:e=>re(e.target.value),placeholder:`Search current page…`,className:`flex-1 text-xs bg-transparent outline-none text-foreground placeholder:text-muted-foreground`}),R&&(0,k.jsx)(`button`,{type:`button`,onClick:()=>re(``),className:`text-muted-foreground hover:text-foreground`,children:(0,k.jsx)(i,{className:`size-3`})})]}),Ne&&(0,k.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs`,children:[(0,k.jsxs)(`span`,{className:`text-muted-foreground`,children:[z.size,` selected`]}),S&&q&&(ce?(0,k.jsxs)(`span`,{className:`flex items-center gap-1`,children:[(0,k.jsxs)(`button`,{type:`button`,onClick:ke,className:`text-destructive text-[10px] font-medium hover:underline`,children:[`Delete `,z.size,`?`]}),(0,k.jsx)(`button`,{type:`button`,onClick:()=>U(!1),className:`text-muted-foreground text-[10px] hover:underline`,children:`Cancel`})]}):(0,k.jsx)(`button`,{type:`button`,onClick:()=>U(!0),className:`p-0.5 text-muted-foreground hover:text-destructive`,children:(0,k.jsx)(p,{className:`size-3`})})),(0,k.jsx)(j,{columns:e.columns,rows:Array.from(z).map(t=>e.rows[t]).filter(Boolean),filename:`${O??`db`}-selected`})]}),C&&(0,k.jsx)(`button`,{type:`button`,onClick:()=>{ae(!0),V({}),H(null)},className:`p-0.5 rounded text-muted-foreground hover:text-primary transition-colors`,title:`Insert row`,children:(0,k.jsx)(h,{className:`size-3.5`})})]}),ie&&(0,k.jsxs)(`div`,{className:`px-2 py-1.5 border-b border-border bg-muted/30 text-xs space-y-1`,children:[(0,k.jsx)(`div`,{className:`flex flex-wrap gap-1.5`,children:t.filter(e=>!e.pk).map(e=>(0,k.jsxs)(`div`,{className:`flex items-center gap-1`,children:[(0,k.jsx)(`label`,{className:`text-muted-foreground text-[10px] w-16 truncate`,title:e.name,children:e.name}),(0,k.jsx)(`input`,{value:oe[e.name]??``,onChange:t=>V(n=>({...n,[e.name]:t.target.value})),placeholder:e.defaultValue??(e.nullable?`NULL`:``),className:`h-5 w-24 text-[11px] px-1 rounded border border-border bg-background outline-none focus:border-primary`})]},e.name))}),(0,k.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,k.jsx)(`button`,{type:`button`,onClick:Ae,className:`text-[10px] px-2 py-0.5 rounded bg-primary text-primary-foreground hover:bg-primary/90`,children:`Save`}),(0,k.jsx)(`button`,{type:`button`,onClick:()=>ae(!1),className:`text-[10px] text-muted-foreground hover:underline`,children:`Cancel`}),se&&(0,k.jsx)(`span`,{className:`text-[10px] text-destructive`,children:se})]})]}),(0,k.jsx)(`div`,{className:`flex-1 overflow-auto`,children:(0,k.jsxs)(`table`,{className:`w-full text-xs`,style:{borderCollapse:`separate`,borderSpacing:0},children:[(0,k.jsx)(`thead`,{ref:Fe,className:`sticky top-0 z-20 bg-muted`,children:(0,k.jsxs)(`tr`,{children:[q&&(0,k.jsx)(`th`,{"data-col":`_cb`,className:`px-2 py-1.5 text-left font-medium text-muted-foreground border-b border-border w-10 bg-muted`,style:{position:`sticky`,left:0,zIndex:30},children:(0,k.jsx)(`input`,{type:`checkbox`,checked:Pe,onChange:Oe,className:`size-3 accent-primary`})}),X.map(e=>{let n=t.find(t=>t.name===e)?.pk,a=m===e,o=W.has(e),s=!!K[e],c=me===e,l=Re.get(e);return(0,k.jsxs)(`th`,{"data-col":e,className:`group/th px-2 py-1.5 text-left font-medium text-muted-foreground border-b border-border whitespace-nowrap bg-muted ${o?`border-r border-r-primary/20`:``}`,style:l==null?void 0:{position:`sticky`,left:l,zIndex:25},children:[(0,k.jsxs)(`div`,{className:`flex items-center gap-0.5`,children:[(0,k.jsxs)(`button`,{type:`button`,onClick:()=>y(e),className:`flex items-center gap-0.5 ${n?`font-bold`:``} hover:text-foreground transition-colors`,children:[e,a&&g===`ASC`&&(0,k.jsx)(d,{className:`size-3`}),a&&g===`DESC`&&(0,k.jsx)(r,{className:`size-3`})]}),A&&(0,k.jsx)(`button`,{type:`button`,title:`Filter column`,onClick:()=>he(c?null:e),className:`p-0.5 rounded transition-colors ${s||c?`text-primary`:`text-muted-foreground/40 md:opacity-0 md:group-hover/th:opacity-100 hover:text-foreground`}`,children:(0,k.jsx)(w,{className:`size-2.5`})}),(0,k.jsx)(`button`,{type:`button`,title:o?`Unpin column`:`Pin column`,onClick:()=>Ce(e),className:`p-0.5 rounded transition-colors ${o?`text-primary`:`text-muted-foreground/40 md:opacity-0 md:group-hover/th:opacity-100 hover:text-foreground`}`,children:o?(0,k.jsx)(b,{className:`size-2.5`}):(0,k.jsx)(_,{className:`size-2.5`})})]}),c&&(0,k.jsxs)(`div`,{className:`mt-1 flex items-center gap-1`,children:[(0,k.jsx)(`input`,{autoFocus:!0,type:`text`,value:K[e]??``,placeholder:`ILIKE filter…`,onChange:t=>Te(e,t.target.value),onKeyDown:e=>{e.key===`Escape`&&he(null)},className:`w-full h-5 text-[10px] px-1 rounded border border-border bg-background outline-none focus:border-primary`}),s&&(0,k.jsx)(`button`,{type:`button`,onClick:()=>Te(e,``),className:`text-muted-foreground hover:text-foreground`,children:(0,k.jsx)(i,{className:`size-2.5`})})]})]},e)}),l&&q&&(0,k.jsx)(`th`,{className:`px-2 py-1.5 border-b border-border w-14 bg-muted`})]})}),(0,k.jsxs)(`tbody`,{children:[$.map(({idx:e,row:t})=>(0,k.jsx)(ne,{row:t,rowIdx:e,columns:X,selected:z.has(e),onToggleSelect:De,pkCol:q,editingCell:M,editValue:P,onStartEdit:ye,onCommitEdit:be,onCancelEdit:xe,onSetEditValue:F,showDelete:!!l,confirmingDelete:I===e,onDelete:Se,onConfirmDelete:L,onViewCell:ue,pinned:!0,onTogglePin:we,pinnedCols:W,pinnedColOffsets:Re,stickyTop:Ue.get(e)??Z,trRef:t=>He(e,t)},`pin-${e}`)),Y.map((e,t)=>G.has(t)?null:(0,k.jsx)(ne,{row:e,rowIdx:t,columns:X,selected:z.has(t),onToggleSelect:De,pkCol:q,editingCell:M,editValue:P,onStartEdit:ye,onCommitEdit:be,onCancelEdit:xe,onSetEditValue:F,showDelete:!!l,confirmingDelete:I===t,onDelete:Se,onConfirmDelete:L,onViewCell:ue,pinned:!1,onTogglePin:we,pinnedCols:W,pinnedColOffsets:Re},t)),Y.length===0&&(0,k.jsx)(`tr`,{children:(0,k.jsx)(`td`,{colSpan:X.length+2,className:`px-2 py-8 text-center text-muted-foreground`,children:`No data`})})]})]})}),(0,k.jsxs)(`div`,{className:`flex items-center justify-between px-3 py-1.5 border-t border-border bg-background shrink-0 text-xs text-muted-foreground`,children:[(0,k.jsxs)(`span`,{children:[e.total.toLocaleString(),` rows`]}),(0,k.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,k.jsx)(`button`,{type:`button`,disabled:o<=1,onClick:()=>s(o-1),className:`p-0.5 rounded hover:bg-muted disabled:opacity-30`,children:(0,k.jsx)(f,{className:`size-3.5`})}),(0,k.jsxs)(`span`,{children:[o,` / `,Me]}),(0,k.jsx)(`button`,{type:`button`,disabled:o>=Me,onClick:()=>s(o+1),className:`p-0.5 rounded hover:bg-muted disabled:opacity-30`,children:(0,k.jsx)(a,{className:`size-3.5`})})]})]})]})}function N(e){return e==null?`NULL`:typeof e==`object`?JSON.stringify(e):String(e)}var P=200;function F(e){if(e==null)return!1;if(typeof e==`object`)return!0;let t=String(e);if(t.length>=P)return!0;let n=t.trimStart();return!!((n[0]===`{`||n[0]===`[`)&&(n.endsWith(`}`)||n.endsWith(`]`))||n.startsWith(`<?xml`)||n.startsWith(`<`)&&n.endsWith(`>`))}function te(e){let t=e.trimStart();if(t[0]===`{`||t[0]===`[`)try{return JSON.parse(t),`json`}catch{}return t.startsWith(`<?xml`)||t.startsWith(`<`)&&/<\/\w+>/.test(t)?`xml`:t.startsWith(`---`)||/^\w+:\s/m.test(t)?`yaml`:`plaintext`}var ne=(0,T.memo)(function({row:e,rowIdx:t,columns:n,selected:r,onToggleSelect:i,pkCol:a,editingCell:o,editValue:s,onStartEdit:l,onCommitEdit:u,onCancelEdit:d,onSetEditValue:f,showDelete:m,confirmingDelete:h,onDelete:g,onConfirmDelete:v,onViewCell:y,pinned:x,onTogglePin:S,pinnedCols:C,pinnedColOffsets:w,stickyTop:T,trRef:E}){let D=x?`bg-muted`:r?`bg-primary/5`:`bg-background`;return(0,k.jsxs)(`tr`,{ref:E,className:`group ${x?``:`hover:bg-muted/30`}`,style:x?{position:`sticky`,top:T,zIndex:15}:void 0,children:[a&&(0,k.jsx)(`td`,{className:`px-1 py-1 border-b border-border/50 ${D}`,style:{position:`sticky`,left:0,zIndex:12},children:(0,k.jsxs)(`span`,{className:`flex items-center gap-0.5`,children:[(0,k.jsx)(`input`,{type:`checkbox`,checked:r,onChange:()=>i(t),className:`size-3 accent-primary`}),(0,k.jsx)(`button`,{type:`button`,title:x?`Unpin row`:`Pin row`,onClick:()=>S(t),className:`p-0.5 rounded transition-colors ${x?`text-primary`:`text-muted-foreground/30 md:opacity-0 md:group-hover:opacity-100 hover:text-foreground`}`,children:x?(0,k.jsx)(b,{className:`size-2.5`}):(0,k.jsx)(_,{className:`size-2.5`})})]})}),n.map(n=>{let r=o?.rowIdx===t&&o?.col===n,i=e[n],p=!r&&F(i),m=C.has(n),h=w.get(n);return(0,k.jsx)(`td`,{className:`px-2 py-1 max-w-[300px] border-b border-border/50 ${m?`border-r border-r-primary/20`:``} ${m||x?D:``}`,style:h==null?void 0:{position:`sticky`,left:h,zIndex:10},children:r?(0,k.jsx)(`input`,{autoFocus:!0,className:`w-full bg-transparent border border-primary/50 rounded px-1 py-0 text-xs outline-none`,value:s,onChange:e=>f(e.target.value),onBlur:u,onKeyDown:e=>{e.key===`Enter`&&u(),e.key===`Escape`&&d()}}):(0,k.jsxs)(`span`,{className:`flex items-center gap-0.5`,children:[(0,k.jsx)(`span`,{className:`cursor-pointer truncate flex-1 ${i==null?`text-muted-foreground/40 italic`:``}`,onDoubleClick:()=>a&&l(t,n,i),title:N(i),children:N(i)}),p&&(0,k.jsx)(`button`,{type:`button`,title:`View full content`,onClick:()=>y({col:n,value:N(i)}),className:`shrink-0 p-0.5 rounded text-muted-foreground/50 hover:text-foreground transition-colors`,children:(0,k.jsx)(c,{className:`size-3`})})]})},n)}),m&&a&&(0,k.jsx)(`td`,{className:`px-2 py-1 border-b border-border/50 ${x?D:``}`,children:h?(0,k.jsxs)(`span`,{className:`flex items-center gap-1 whitespace-nowrap`,children:[(0,k.jsx)(`button`,{type:`button`,onClick:()=>g(t),className:`text-destructive text-[10px] font-medium hover:underline`,children:`Confirm`}),(0,k.jsx)(`button`,{type:`button`,onClick:()=>v(null),className:`text-muted-foreground text-[10px] hover:underline`,children:`Cancel`})]}):(0,k.jsx)(`button`,{type:`button`,onClick:()=>v(t),className:`p-0.5 rounded md:opacity-0 md:group-hover:opacity-100 hover:bg-destructive/10 hover:text-destructive transition-opacity`,title:`Delete row`,children:(0,k.jsx)(p,{className:`size-3`})})})]})});function I({metadata:e}){let t=e?.connectionId,n=e?.connectionName,r=e?.tableName,i=e?.schemaName??`public`,a=e?.initialSql,o=O(t),[c,u]=(0,T.useState)([]),[d,f]=(0,T.useState)(180),p=(0,T.useRef)(null),[m,h]=(0,T.useState)({}),_=(0,T.useMemo)(()=>{if(a&&!o.selectedTable)return a;if(o.selectedTable){let e=`SELECT * FROM "${o.selectedTable}"`,t=Object.entries(m).filter(([,e])=>e.trim()).map(([e,t])=>`"${e}" ILIKE '%${t.replace(/'/g,`''`)}%'`);t.length>0&&(e+=` WHERE ${t.join(` AND `)}`),o.orderBy&&(e+=` ORDER BY "${o.orderBy}" ${o.orderDir}`);let n=(o.page-1)*100;return e+=` LIMIT 100`,n>0&&(e+=` OFFSET ${n}`),e}return`SELECT * FROM `},[a,o.selectedTable,o.orderBy,o.orderDir,o.page,m]),v=(0,T.useCallback)(e=>{h(e)},[]),b=(0,T.useRef)(void 0);(0,T.useEffect)(()=>{if(!(!o.selectedTable||Object.keys(m).length===0))return clearTimeout(b.current),b.current=setTimeout(()=>{o.executeQuery(_),D(!0)},500),()=>clearTimeout(b.current)},[m]);let x=(0,T.useCallback)(e=>{e.preventDefault();let t=e.clientY,n=d,r=e=>{let r=e.clientY-t;f(Math.max(80,Math.min(n+r,(p.current?.clientHeight??600)-100)))},i=()=>{document.removeEventListener(`mousemove`,r),document.removeEventListener(`mouseup`,i)};document.addEventListener(`mousemove`,r),document.addEventListener(`mouseup`,i)},[d]);(0,T.useEffect)(()=>{s.get(`/api/db/connections/${t}/tables?cached=1`).then(e=>u(e.map(e=>({name:e.name,schema:e.schema})))).catch(()=>{})},[t]);let C=(0,T.useMemo)(()=>{if(c.length!==0)return{tables:c,getColumns:async(e,n)=>await s.get(`/api/db/connections/${t}/schema?table=${encodeURIComponent(e)}${n?`&schema=${encodeURIComponent(n)}`:``}`)}},[t,c]),w=(0,T.useRef)(!1);(0,T.useEffect)(()=>{w.current||(w.current=!0,a?o.executeQuery(a):r&&o.selectTable(r,i))},[r,i,a]);let[E,D]=(0,T.useState)(!!a),ee=(0,T.useCallback)(e=>{D(!0),o.executeQuery(e)},[o.executeQuery]),A=(0,T.useCallback)(e=>{D(!1),o.toggleSort(e)},[o.toggleSort]),N=(0,T.useCallback)(e=>{D(!1),o.setPage(e)},[o.setPage]),P=o.queryResult,F=E&&!!(P||o.queryError),te=o.selectedTable&&!F;return(0,k.jsx)(`div`,{ref:p,className:`flex h-full w-full overflow-hidden`,children:(0,k.jsxs)(`div`,{className:`flex-1 flex flex-col overflow-hidden`,children:[(0,k.jsxs)(`div`,{className:`flex items-center gap-2 px-3 py-1.5 border-b border-border bg-background shrink-0`,children:[(0,k.jsx)(l,{className:`size-3.5 text-muted-foreground`}),(0,k.jsx)(`span`,{className:`text-xs text-muted-foreground truncate`,children:n??`Database`}),o.selectedTable&&(0,k.jsxs)(`span`,{className:`text-xs text-muted-foreground`,children:[`/ `,o.selectedTable]}),(0,k.jsxs)(`div`,{className:`ml-auto flex items-center gap-1`,children:[o.tableData&&(0,k.jsx)(j,{columns:o.tableData.columns,rows:o.tableData.rows,filename:n?`${n}-${o.selectedTable??`data`}`:o.selectedTable??`data`,exportAllUrl:o.selectedTable?`/api/db/connections/${t}/export?table=${encodeURIComponent(o.selectedTable)}&schema=${o.selectedSchema}`:void 0}),(0,k.jsx)(`button`,{type:`button`,onClick:()=>o.refreshData(),title:`Reload data`,className:`p-1 rounded text-muted-foreground hover:text-foreground transition-colors`,children:(0,k.jsx)(y,{className:`size-3 ${o.loading?`animate-spin`:``}`})})]})]}),(0,k.jsx)(`div`,{className:`shrink-0 border-b border-border`,style:{height:d},children:(0,k.jsx)(S,{onExecute:ee,loading:o.queryLoading,defaultValue:_,schemaInfo:C})}),(0,k.jsx)(`div`,{onMouseDown:x,className:`shrink-0 h-1.5 cursor-row-resize bg-border/50 hover:bg-primary/30 flex items-center justify-center transition-colors`,children:(0,k.jsx)(g,{className:`size-3 text-muted-foreground/50`})}),(0,k.jsxs)(`div`,{className:`flex-1 overflow-hidden`,children:[te&&(0,k.jsx)(M,{tableData:o.tableData,schema:o.schema,loading:o.loading,page:o.page,onPageChange:N,onCellUpdate:o.updateCell,onRowDelete:o.deleteRow,orderBy:o.orderBy,orderDir:o.orderDir,onToggleSort:A,onBulkDelete:o.bulkDelete,onInsertRow:o.insertRow,connectionId:t,selectedTable:o.selectedTable,selectedSchema:o.selectedSchema,connectionName:n,onColumnFilter:v}),F&&(0,k.jsx)(R,{result:P,error:o.queryError,loading:o.queryLoading,schema:o.schema,connectionName:n})]})]})})}var L=()=>{};function R({result:e,error:t,loading:n,schema:r,connectionName:i}){let a=(0,T.useMemo)(()=>e?.changeType===`select`&&e.rows.length>0?{columns:e.columns,rows:e.rows,total:e.rows.length,limit:e.rows.length}:null,[e]),o=(0,T.useMemo)(()=>r?.length?r:(e?.columns??[]).map(e=>({name:e,type:`text`,nullable:!0,pk:!1,defaultValue:null})),[r,e?.columns]);return(0,k.jsxs)(`div`,{className:`flex flex-col h-full overflow-hidden text-xs`,children:[t&&(0,k.jsx)(`div`,{className:`px-3 py-2 text-destructive bg-destructive/5 shrink-0`,children:t}),e?.changeType===`modify`&&(0,k.jsxs)(`div`,{className:`px-3 py-2 text-green-500 shrink-0`,children:[e.rowsAffected,` row(s) affected`,e.executionTimeMs!=null&&(0,k.jsxs)(`span`,{className:`text-muted-foreground ml-2`,children:[e.executionTimeMs,`ms`]})]}),a&&(0,k.jsxs)(`div`,{className:`flex-1 overflow-hidden`,children:[(0,k.jsx)(M,{tableData:a,schema:o,loading:!!n,page:1,onPageChange:L,onCellUpdate:L,orderBy:null,orderDir:`ASC`,onToggleSort:L,connectionName:i}),e?.executionTimeMs!=null&&(0,k.jsxs)(`div`,{className:`px-3 py-0.5 border-t border-border text-[10px] text-muted-foreground shrink-0`,children:[e.rows.length,` rows · `,e.executionTimeMs,`ms`]})]}),e?.changeType===`select`&&e.rows.length===0&&(0,k.jsxs)(`div`,{className:`px-3 py-2 text-muted-foreground shrink-0`,children:[`No results`,e.executionTimeMs!=null&&(0,k.jsxs)(`span`,{className:`ml-2 text-muted-foreground/60`,children:[e.executionTimeMs,`ms`]})]}),!e&&!t&&(0,k.jsx)(`div`,{className:`flex items-center justify-center h-full text-muted-foreground`,children:n?(0,k.jsx)(v,{className:`size-4 animate-spin`}):`Run a query to see results`})]})}export{I as DatabaseViewer};
|