sqlite-hub 0.8.7 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +16 -0
- package/frontend/index.html +1 -1
- package/frontend/js/api.js +67 -0
- package/frontend/js/app.js +1554 -999
- package/frontend/js/components/modal.js +208 -3
- package/frontend/js/components/queryEditor.js +9 -4
- package/frontend/js/components/queryHistoryDetail.js +34 -12
- package/frontend/js/components/queryHistoryPanel.js +8 -11
- package/frontend/js/components/rowEditorPanel.js +26 -5
- package/frontend/js/components/sidebar.js +49 -5
- package/frontend/js/components/structureGraph.js +614 -632
- package/frontend/js/components/tableDesignerSqlPreview.js +24 -25
- package/frontend/js/lib/mediaTaggingDefaults.js +27 -0
- package/frontend/js/router.js +81 -71
- package/frontend/js/store.js +3140 -2179
- package/frontend/js/views/charts.js +2 -2
- package/frontend/js/views/data.js +28 -14
- package/frontend/js/views/editor.js +172 -177
- package/frontend/js/views/mediaTagging.js +931 -0
- package/frontend/styles/base.css +11 -1
- package/frontend/styles/components.css +54 -6
- package/frontend/styles/views.css +910 -0
- package/package.json +1 -1
- package/server/routes/charts.js +4 -1
- package/server/routes/data.js +14 -0
- package/server/routes/mediaTagging.js +166 -0
- package/server/routes/sql.js +1 -0
- package/server/server.js +4 -0
- package/server/services/sqlite/dataBrowserService.js +25 -0
- package/server/services/sqlite/exportService.js +31 -1
- package/server/services/sqlite/mediaTaggingService.js +1689 -0
- package/server/services/storage/appStateStore.js +325 -3
- package/server/services/storage/queryHistoryUtils.js +165 -2
- package/shortkeys.md +5 -0
|
@@ -50,10 +50,10 @@ function renderChartsList(state) {
|
|
|
50
50
|
<div>
|
|
51
51
|
<span class="material-symbols-outlined mb-3 text-4xl text-on-surface-variant/25">query_stats</span>
|
|
52
52
|
<p class="font-headline text-lg font-black uppercase tracking-tight text-on-surface">
|
|
53
|
-
No
|
|
53
|
+
No Chartable Queries
|
|
54
54
|
</p>
|
|
55
55
|
<p class="mt-2 max-w-xs text-sm leading-6 text-on-surface-variant/60">
|
|
56
|
-
Run
|
|
56
|
+
Run SELECT queries in the SQL Editor first. They will appear here automatically.
|
|
57
57
|
</p>
|
|
58
58
|
</div>
|
|
59
59
|
</div>
|
|
@@ -3,6 +3,10 @@ import { renderRowEditorPanel } from '../components/rowEditorPanel.js';
|
|
|
3
3
|
import { escapeHtml, formatCellValue, formatNumber, isBlobPreview, truncateMiddle } from '../utils/format.js';
|
|
4
4
|
|
|
5
5
|
function getSelectedRow(state) {
|
|
6
|
+
if (state.dataBrowser.selectedRow) {
|
|
7
|
+
return state.dataBrowser.selectedRow;
|
|
8
|
+
}
|
|
9
|
+
|
|
6
10
|
const rowIndex = state.dataBrowser.selectedRowIndex;
|
|
7
11
|
|
|
8
12
|
if (typeof rowIndex !== 'number') {
|
|
@@ -121,7 +125,7 @@ function renderWorkspaceHeader(state) {
|
|
|
121
125
|
<button
|
|
122
126
|
class="standard-button"
|
|
123
127
|
data-action="navigate"
|
|
124
|
-
data-to="/structure"
|
|
128
|
+
data-to="/structure/${encodeURIComponent(table.name)}"
|
|
125
129
|
type="button"
|
|
126
130
|
>
|
|
127
131
|
<span class="material-symbols-outlined">account_tree</span>
|
|
@@ -341,13 +345,9 @@ function renderTableSurface(state) {
|
|
|
341
345
|
getRowClass: (_, filteredIndex) => {
|
|
342
346
|
const rowIndex = filteredRows[filteredIndex]?.index ?? filteredIndex;
|
|
343
347
|
|
|
344
|
-
return
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
: filteredIndex % 2 === 0
|
|
348
|
-
? 'bg-surface-container-low'
|
|
349
|
-
: 'bg-surface-container-lowest'
|
|
350
|
-
} cursor-pointer transition-colors hover:bg-surface-container-high`;
|
|
348
|
+
return `data-browser-row ${
|
|
349
|
+
filteredIndex % 2 === 0 ? 'data-browser-row--even' : 'data-browser-row--odd'
|
|
350
|
+
} ${state.dataBrowser.selectedRowIndex === rowIndex ? 'is-selected' : ''} cursor-pointer transition-colors`;
|
|
351
351
|
},
|
|
352
352
|
getRowAttrs: (_, filteredIndex) => {
|
|
353
353
|
const rowIndex = filteredRows[filteredIndex]?.index ?? filteredIndex;
|
|
@@ -434,15 +434,23 @@ function renderTableSurface(state) {
|
|
|
434
434
|
`;
|
|
435
435
|
}
|
|
436
436
|
|
|
437
|
-
function renderDataRowEditorPanel(state) {
|
|
437
|
+
export function renderDataRowEditorPanel(state) {
|
|
438
438
|
const table = state.dataBrowser.table;
|
|
439
439
|
const rowIndex = state.dataBrowser.selectedRowIndex;
|
|
440
440
|
const row = getSelectedRow(state);
|
|
441
|
+
const isIndexedRow = typeof rowIndex === 'number';
|
|
441
442
|
|
|
442
|
-
if (!table || !row
|
|
443
|
+
if (!table || !row) {
|
|
443
444
|
return '';
|
|
444
445
|
}
|
|
445
446
|
|
|
447
|
+
const foreignKeyColumnNames = new Set(
|
|
448
|
+
(table.foreignKeys ?? []).flatMap(foreignKey =>
|
|
449
|
+
(foreignKey.mappings ?? []).map(mapping => String(mapping.from ?? '').trim()).filter(Boolean),
|
|
450
|
+
),
|
|
451
|
+
);
|
|
452
|
+
const getColumnBadges = column => (foreignKeyColumnNames.has(column.name) ? ['FK'] : []);
|
|
453
|
+
|
|
446
454
|
const identityColumns = table.identityStrategy?.type === 'primaryKey' ? (table.identityStrategy.columns ?? []) : [];
|
|
447
455
|
const editableColumns = (table.columnMeta ?? []).filter(column => {
|
|
448
456
|
if (!column.visible || column.generated) {
|
|
@@ -476,10 +484,12 @@ function renderDataRowEditorPanel(state) {
|
|
|
476
484
|
return renderRowEditorPanel({
|
|
477
485
|
title: table.name,
|
|
478
486
|
sectionLabel: 'Row Editor',
|
|
479
|
-
subtitle: `row ${rowIndex + 1}
|
|
487
|
+
subtitle: isIndexedRow ? `row ${rowIndex + 1}` : 'targeted row',
|
|
480
488
|
closeAction: 'clear-data-row-selection',
|
|
481
489
|
formName: 'save-data-row',
|
|
482
|
-
hiddenFields:
|
|
490
|
+
hiddenFields: isIndexedRow
|
|
491
|
+
? [{ name: 'rowIndex', value: String(rowIndex) }]
|
|
492
|
+
: [{ name: 'rowIdentity', value: JSON.stringify(row.__identity ?? null) }],
|
|
483
493
|
disabledMessage: state.connections.active?.readOnly
|
|
484
494
|
? 'The active database is opened read-only, so row editing is disabled.'
|
|
485
495
|
: table.notSafelyUpdatable
|
|
@@ -491,19 +501,23 @@ function renderDataRowEditorPanel(state) {
|
|
|
491
501
|
return {
|
|
492
502
|
name: column.name,
|
|
493
503
|
label: column.name,
|
|
504
|
+
badges: getColumnBadges(column),
|
|
494
505
|
value: value === null || value === undefined ? '' : String(value),
|
|
495
506
|
};
|
|
496
507
|
}),
|
|
497
508
|
readonlyFields: readonlyColumns.map(column => ({
|
|
498
509
|
name: column.name,
|
|
499
|
-
label:
|
|
510
|
+
label: {
|
|
511
|
+
label: column.name,
|
|
512
|
+
badges: getColumnBadges(column),
|
|
513
|
+
},
|
|
500
514
|
value: formatCellValue(row[column.name]),
|
|
501
515
|
})),
|
|
502
516
|
saveError: state.dataBrowser.saveError,
|
|
503
517
|
saving: state.dataBrowser.saving,
|
|
504
518
|
deleting: state.dataBrowser.deleting,
|
|
505
519
|
deleteAction: 'delete-data-row',
|
|
506
|
-
deleteRowIndex: rowIndex,
|
|
520
|
+
deleteRowIndex: isIndexedRow ? rowIndex : null,
|
|
507
521
|
deleteEnabled: Boolean(row.__identity),
|
|
508
522
|
reloadAction: 'reload-data-route',
|
|
509
523
|
});
|
|
@@ -1,19 +1,14 @@
|
|
|
1
|
-
import { renderBottomTabs } from
|
|
2
|
-
import { renderQueryEditor } from
|
|
3
|
-
import { renderQueryHistoryDetail } from
|
|
4
|
-
import { renderQueryHistoryPanel } from
|
|
5
|
-
import { renderRowEditorPanel } from
|
|
6
|
-
import { renderQueryResultsPane } from
|
|
7
|
-
import { getCurrentConnection, getQueryMessages, getQueryPerformance } from
|
|
8
|
-
import {
|
|
9
|
-
escapeHtml,
|
|
10
|
-
formatCellValue,
|
|
11
|
-
formatNumber,
|
|
12
|
-
isBlobPreview,
|
|
13
|
-
} from "../utils/format.js";
|
|
1
|
+
import { renderBottomTabs } from '../components/bottomTabs.js';
|
|
2
|
+
import { renderQueryEditor } from '../components/queryEditor.js';
|
|
3
|
+
import { renderQueryHistoryDetail } from '../components/queryHistoryDetail.js';
|
|
4
|
+
import { renderQueryHistoryPanel } from '../components/queryHistoryPanel.js';
|
|
5
|
+
import { renderRowEditorPanel } from '../components/rowEditorPanel.js';
|
|
6
|
+
import { renderQueryResultsPane } from '../components/queryResults.js';
|
|
7
|
+
import { getCurrentConnection, getQueryMessages, getQueryPerformance } from '../store.js';
|
|
8
|
+
import { escapeHtml, formatCellValue, formatNumber, isBlobPreview } from '../utils/format.js';
|
|
14
9
|
|
|
15
10
|
function renderMissingDatabase() {
|
|
16
|
-
|
|
11
|
+
return `
|
|
17
12
|
<div class="flex flex-1 flex-col items-center justify-center bg-surface-container-lowest px-8 text-center">
|
|
18
13
|
<span class="material-symbols-outlined mb-3 text-5xl text-on-surface-variant/25">database_off</span>
|
|
19
14
|
<p class="font-headline text-xl font-black uppercase tracking-tight text-primary-container">
|
|
@@ -27,60 +22,60 @@ function renderMissingDatabase() {
|
|
|
27
22
|
}
|
|
28
23
|
|
|
29
24
|
function renderMessagesPane(state) {
|
|
30
|
-
|
|
25
|
+
const items = getQueryMessages(state);
|
|
31
26
|
|
|
32
|
-
|
|
27
|
+
return `
|
|
33
28
|
<div class="custom-scrollbar h-full overflow-auto bg-surface-container-lowest px-6 py-6">
|
|
34
29
|
<div class="space-y-4">
|
|
35
30
|
${items
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
.map(
|
|
32
|
+
item => `
|
|
38
33
|
<div class="border border-outline-variant/10 bg-surface-container-low px-4 py-4">
|
|
39
34
|
<div class="text-[10px] font-mono uppercase tracking-[0.2em] ${
|
|
40
|
-
|
|
35
|
+
item.tone === 'alert' ? 'text-error' : 'text-primary-container'
|
|
41
36
|
}">
|
|
42
37
|
${escapeHtml(item.label)}
|
|
43
38
|
</div>
|
|
44
39
|
<div class="mt-2 text-sm text-on-surface">${escapeHtml(item.value)}</div>
|
|
45
40
|
</div>
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
41
|
+
`,
|
|
42
|
+
)
|
|
43
|
+
.join('')}
|
|
49
44
|
</div>
|
|
50
45
|
</div>
|
|
51
46
|
`;
|
|
52
47
|
}
|
|
53
48
|
|
|
54
49
|
function renderPerformancePane(state) {
|
|
55
|
-
|
|
50
|
+
const metrics = getQueryPerformance(state);
|
|
56
51
|
|
|
57
|
-
|
|
52
|
+
return `
|
|
58
53
|
<div class="grid flex-1 grid-cols-1 gap-4 bg-surface-container-lowest p-6 md:grid-cols-4">
|
|
59
54
|
<div class="metric-card">
|
|
60
55
|
<span class="text-[10px] font-mono uppercase text-on-surface/40">Exec_Time</span>
|
|
61
56
|
<span class="font-headline text-3xl font-bold text-on-surface">${escapeHtml(
|
|
62
|
-
|
|
57
|
+
String(metrics.timingMs ?? 0),
|
|
63
58
|
)}ms</span>
|
|
64
59
|
<span class="text-[10px] text-primary-container">Measured backend execution time</span>
|
|
65
60
|
</div>
|
|
66
61
|
<div class="metric-card">
|
|
67
62
|
<span class="text-[10px] font-mono uppercase text-on-surface/40">Statements</span>
|
|
68
63
|
<span class="font-headline text-3xl font-bold text-on-surface">${escapeHtml(
|
|
69
|
-
|
|
64
|
+
formatNumber(metrics.statementCount),
|
|
70
65
|
)}</span>
|
|
71
66
|
<span class="text-[10px] text-on-surface/40">Split and executed by SQLite</span>
|
|
72
67
|
</div>
|
|
73
68
|
<div class="metric-card">
|
|
74
69
|
<span class="text-[10px] font-mono uppercase text-on-surface/40">Rows_Returned</span>
|
|
75
70
|
<span class="font-headline text-3xl font-bold text-on-surface">${escapeHtml(
|
|
76
|
-
|
|
71
|
+
formatNumber(metrics.rowCount),
|
|
77
72
|
)}</span>
|
|
78
73
|
<span class="text-[10px] text-on-surface/40">Visible result set size</span>
|
|
79
74
|
</div>
|
|
80
75
|
<div class="metric-card metric-card--accent">
|
|
81
76
|
<span class="text-[10px] font-mono uppercase text-on-surface/40">Rows_Affected</span>
|
|
82
77
|
<span class="font-headline text-3xl font-bold text-on-surface">${escapeHtml(
|
|
83
|
-
|
|
78
|
+
formatNumber(metrics.affectedRowCount),
|
|
84
79
|
)}</span>
|
|
85
80
|
<span class="text-[10px] text-primary-container">INSERT / UPDATE / DELETE impact</span>
|
|
86
81
|
</div>
|
|
@@ -89,144 +84,144 @@ function renderPerformancePane(state) {
|
|
|
89
84
|
}
|
|
90
85
|
|
|
91
86
|
function getResultEditingState(state) {
|
|
92
|
-
|
|
87
|
+
const editing = state.editor.result?.editing;
|
|
93
88
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
89
|
+
if (!editing) {
|
|
90
|
+
return {
|
|
91
|
+
enabled: false,
|
|
92
|
+
message: '',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
100
95
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
96
|
+
if (state.connections.active?.readOnly) {
|
|
97
|
+
return {
|
|
98
|
+
enabled: false,
|
|
99
|
+
message: 'The active database is opened read-only, so query result editing is disabled.',
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!editing.enabled) {
|
|
104
|
+
return {
|
|
105
|
+
enabled: false,
|
|
106
|
+
message: editing.reason || 'Only direct single-table SELECT results can be edited here.',
|
|
107
|
+
};
|
|
108
|
+
}
|
|
107
109
|
|
|
108
|
-
if (!editing.enabled) {
|
|
109
110
|
return {
|
|
110
|
-
|
|
111
|
-
|
|
111
|
+
enabled: true,
|
|
112
|
+
message: `Click a row to edit it in ${editing.tableName}.`,
|
|
112
113
|
};
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return {
|
|
116
|
-
enabled: true,
|
|
117
|
-
message: `Click a row to edit it in ${editing.tableName}.`,
|
|
118
|
-
};
|
|
119
114
|
}
|
|
120
115
|
|
|
121
116
|
function getUniqueResultColumns(columns = []) {
|
|
122
|
-
|
|
123
|
-
|
|
117
|
+
const uniqueColumns = [];
|
|
118
|
+
const seen = new Set();
|
|
124
119
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
120
|
+
columns.forEach(column => {
|
|
121
|
+
if (!column?.sourceColumn || seen.has(column.sourceColumn)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
129
124
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
125
|
+
seen.add(column.sourceColumn);
|
|
126
|
+
uniqueColumns.push(column);
|
|
127
|
+
});
|
|
133
128
|
|
|
134
|
-
|
|
129
|
+
return uniqueColumns;
|
|
135
130
|
}
|
|
136
131
|
|
|
137
132
|
function renderEditorRowPanel(state) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
133
|
+
const result = state.editor.result;
|
|
134
|
+
const rowIndex = state.editor.selectedRowIndex;
|
|
135
|
+
const row = typeof rowIndex === 'number' ? (result?.rows?.[rowIndex] ?? null) : null;
|
|
141
136
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const uniqueColumns = getUniqueResultColumns(result.editing?.columns ?? []);
|
|
147
|
-
const editableColumns = uniqueColumns.filter((column) => {
|
|
148
|
-
if (column.identity || column.generated || !column.visible) {
|
|
149
|
-
return false;
|
|
137
|
+
if (!result || !row || typeof rowIndex !== 'number') {
|
|
138
|
+
return '';
|
|
150
139
|
}
|
|
151
140
|
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
141
|
+
const uniqueColumns = getUniqueResultColumns(result.editing?.columns ?? []);
|
|
142
|
+
const editableColumns = uniqueColumns.filter(column => {
|
|
143
|
+
if (column.identity || column.generated || !column.visible) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
156
146
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
return false;
|
|
162
|
-
}
|
|
147
|
+
const value = row[column.resultName];
|
|
148
|
+
if (isBlobPreview(value) || (value && typeof value === 'object')) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
163
151
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
152
|
+
return true;
|
|
153
|
+
});
|
|
154
|
+
const readonlyColumns = uniqueColumns.filter(column => {
|
|
155
|
+
if (!column.visible) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
167
158
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
159
|
+
if (column.identity || column.generated) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const value = row[column.resultName];
|
|
164
|
+
return isBlobPreview(value) || (value && typeof value === 'object');
|
|
165
|
+
});
|
|
166
|
+
const editingState = getResultEditingState(state);
|
|
172
167
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
168
|
+
return renderRowEditorPanel({
|
|
169
|
+
title: result.editing?.tableName ?? 'Query Result',
|
|
170
|
+
sectionLabel: 'Row Editor',
|
|
171
|
+
subtitle: `query row ${rowIndex + 1}`,
|
|
172
|
+
closeAction: 'clear-editor-row-selection',
|
|
173
|
+
formName: 'save-editor-row',
|
|
174
|
+
hiddenFields: [{ name: 'rowIndex', value: String(rowIndex) }],
|
|
175
|
+
disabledMessage: editingState.enabled ? '' : editingState.message,
|
|
176
|
+
editableFields: editableColumns.map(column => {
|
|
177
|
+
const value = row[column.resultName];
|
|
183
178
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
179
|
+
return {
|
|
180
|
+
name: column.sourceColumn,
|
|
181
|
+
label: column.sourceColumn,
|
|
182
|
+
value: value === null || value === undefined ? '' : String(value),
|
|
183
|
+
};
|
|
184
|
+
}),
|
|
185
|
+
readonlyFields: readonlyColumns.map(column => ({
|
|
186
|
+
name: column.sourceColumn,
|
|
187
|
+
label: column.sourceColumn,
|
|
188
|
+
value: formatCellValue(row[column.resultName]),
|
|
189
|
+
})),
|
|
190
|
+
saveError: state.editor.saveError,
|
|
191
|
+
saving: state.editor.saving,
|
|
192
|
+
deleting: state.editor.deleting,
|
|
193
|
+
deleteAction: 'delete-editor-row',
|
|
194
|
+
deleteRowIndex: rowIndex,
|
|
195
|
+
deleteEnabled: editingState.enabled && Boolean(row.__identity),
|
|
196
|
+
});
|
|
202
197
|
}
|
|
203
198
|
|
|
204
199
|
function renderResultsSurface(state, isResultsRoute) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
200
|
+
const activeTab = state.editor.activeTab;
|
|
201
|
+
const counts = {
|
|
202
|
+
resultRows: state.editor.result?.rows?.length ?? 0,
|
|
203
|
+
messages: getQueryMessages(state).length,
|
|
204
|
+
statementCount: state.editor.result?.statementCount ?? 0,
|
|
205
|
+
};
|
|
211
206
|
|
|
212
|
-
|
|
207
|
+
let content = renderMessagesPane(state);
|
|
213
208
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
209
|
+
if (activeTab === 'performance') {
|
|
210
|
+
content = renderPerformancePane(state);
|
|
211
|
+
} else if (activeTab === 'results') {
|
|
212
|
+
const editingState = getResultEditingState(state);
|
|
218
213
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
214
|
+
content = state.connections.active
|
|
215
|
+
? renderQueryResultsPane(state.editor.result, {
|
|
216
|
+
selectedRowIndex: state.editor.selectedRowIndex,
|
|
217
|
+
editable: editingState.enabled,
|
|
218
|
+
sortColumn: state.editor.resultSortColumn,
|
|
219
|
+
sortDirection: state.editor.resultSortDirection,
|
|
220
|
+
})
|
|
221
|
+
: renderMissingDatabase();
|
|
222
|
+
}
|
|
228
223
|
|
|
229
|
-
|
|
224
|
+
return `
|
|
230
225
|
<div class="flex h-full min-h-0 flex-col border-t border-outline-variant/10 bg-surface-container-lowest">
|
|
231
226
|
${renderBottomTabs(state.editor.activeTab, counts)}
|
|
232
227
|
<div class="min-h-0 flex-1">${content}</div>
|
|
@@ -235,26 +230,26 @@ function renderResultsSurface(state, isResultsRoute) {
|
|
|
235
230
|
}
|
|
236
231
|
|
|
237
232
|
export function renderEditorView(state, { isResultsRoute = false } = {}) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
233
|
+
const connection = getCurrentConnection(state);
|
|
234
|
+
const editorSectionClass = state.editor.editorPanelVisible ? 'min-h-[27.5%] max-h-[60%]' : 'flex-none';
|
|
235
|
+
const resultsSectionClass = 'flex-1';
|
|
241
236
|
|
|
242
|
-
|
|
243
|
-
|
|
237
|
+
return {
|
|
238
|
+
main: `
|
|
244
239
|
<section class="view-surface flex h-full min-h-0 flex-col overflow-hidden xl:flex-row">
|
|
245
240
|
<div class="flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden">
|
|
246
241
|
<section class="${editorSectionClass} flex min-h-0 flex-col ${
|
|
247
|
-
|
|
242
|
+
isResultsRoute ? 'border-b-4 border-background' : ''
|
|
248
243
|
}">
|
|
249
244
|
${renderQueryEditor({
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
245
|
+
query: state.editor.sqlText,
|
|
246
|
+
executing: state.editor.executing,
|
|
247
|
+
exporting: state.editor.exportLoading,
|
|
248
|
+
historyLoading: state.editor.historyLoading,
|
|
249
|
+
historyTotal: state.editor.historyTotal,
|
|
250
|
+
editorVisible: state.editor.editorPanelVisible,
|
|
251
|
+
historyVisible: state.editor.historyPanelVisible,
|
|
252
|
+
title: connection?.label ?? 'SQLite Query Workspace',
|
|
258
253
|
})}
|
|
259
254
|
</section>
|
|
260
255
|
<section class="${resultsSectionClass} flex min-h-0 min-w-0 flex-col overflow-hidden">
|
|
@@ -262,33 +257,33 @@ export function renderEditorView(state, { isResultsRoute = false } = {}) {
|
|
|
262
257
|
</section>
|
|
263
258
|
</div>
|
|
264
259
|
${
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
260
|
+
state.editor.historyPanelVisible
|
|
261
|
+
? renderQueryHistoryPanel({
|
|
262
|
+
items: state.editor.history,
|
|
263
|
+
loading: state.editor.historyLoading,
|
|
264
|
+
loadingMore: state.editor.historyLoadingMore,
|
|
265
|
+
error: state.editor.historyError,
|
|
266
|
+
activeTab: state.editor.historyTab,
|
|
267
|
+
search: state.editor.historySearchInput,
|
|
268
|
+
total: state.editor.historyTotal,
|
|
269
|
+
hasMore: state.editor.historyHasMore,
|
|
270
|
+
activeHistoryId: state.editor.historyActiveId,
|
|
271
|
+
selectedHistoryId: state.editor.historySelectedId,
|
|
272
|
+
})
|
|
273
|
+
: ''
|
|
279
274
|
}
|
|
280
275
|
</section>
|
|
281
276
|
`,
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
277
|
+
panel:
|
|
278
|
+
isResultsRoute && state.editor.selectedRowIndex !== null
|
|
279
|
+
? renderEditorRowPanel(state)
|
|
280
|
+
: state.editor.historySelectedId
|
|
281
|
+
? renderQueryHistoryDetail({
|
|
282
|
+
item: state.editor.historyDetail,
|
|
283
|
+
runs: state.editor.historyRuns,
|
|
284
|
+
loading: state.editor.historyDetailLoading,
|
|
285
|
+
error: state.editor.historyDetailError,
|
|
286
|
+
})
|
|
287
|
+
: '',
|
|
288
|
+
};
|
|
294
289
|
}
|