sqlite-hub 0.9.12 → 0.10.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/frontend/js/api.js +7 -0
- package/frontend/js/app.js +75 -11
- package/frontend/js/components/modal.js +98 -1
- package/frontend/js/components/queryEditor.js +1 -8
- package/frontend/js/components/structureGraph.js +29 -1
- package/frontend/js/store.js +299 -39
- package/frontend/js/views/charts.js +2 -2
- package/frontend/js/views/data.js +39 -1
- package/frontend/js/views/editor.js +11 -6
- package/frontend/js/views/structure.js +43 -5
- package/frontend/styles/tailwind.generated.css +1 -1
- package/frontend/styles/views.css +4 -0
- package/package.json +1 -1
- package/server/routes/data.js +14 -0
- package/server/services/sqlite/dataBrowserService.js +124 -44
- package/tests/sql-identifier-safety.test.js +48 -8
package/frontend/js/store.js
CHANGED
|
@@ -34,9 +34,21 @@ const DATA_PAGE_SIZES = [25, 50, 100];
|
|
|
34
34
|
const DATA_ROW_SIZE_STORAGE_KEY = 'data_row_size';
|
|
35
35
|
const CHARTS_HISTORY_TAB_STORAGE_KEY = 'charts_history_tab';
|
|
36
36
|
const QUERY_HISTORY_TAB_STORAGE_KEY = 'query_history_tab';
|
|
37
|
+
const UI_PREFERENCE_STORAGE_KEYS = {
|
|
38
|
+
sqlEditorHistoryVisible: 'sqlite_hub_sql_editor_history_visible',
|
|
39
|
+
sqlEditorEditorVisible: 'sqlite_hub_sql_editor_editor_visible',
|
|
40
|
+
sqlEditorActiveTab: 'sqlite_hub_sql_editor_active_tab',
|
|
41
|
+
sqlEditorQueryDraft: 'sqlite_hub_sql_editor_query_draft',
|
|
42
|
+
dataTablesVisible: 'sqlite_hub_data_tables_visible',
|
|
43
|
+
structureTablesVisible: 'sqlite_hub_structure_tables_visible',
|
|
44
|
+
chartsHistoryVisible: 'sqlite_hub_charts_history_visible',
|
|
45
|
+
chartsResultsVisible: 'sqlite_hub_charts_results_visible',
|
|
46
|
+
tableDesignerSqlPreviewVisible: 'sqlite_hub_table_designer_sql_preview_visible',
|
|
47
|
+
};
|
|
37
48
|
const QUERY_HISTORY_PAGE_SIZE = 30;
|
|
38
49
|
const QUERY_HISTORY_RUN_LIMIT = 8;
|
|
39
50
|
const CHART_HEIGHT_PRESETS = new Set(['small', 'medium', 'large']);
|
|
51
|
+
const EDITOR_RESULT_TABS = new Set(['results', 'performance', 'messages']);
|
|
40
52
|
const MISSING_DATABASE_ERROR = {
|
|
41
53
|
code: 'ACTIVE_DATABASE_REQUIRED',
|
|
42
54
|
message: 'No active SQLite database selected.',
|
|
@@ -66,6 +78,70 @@ function storeDataPageSize(pageSize) {
|
|
|
66
78
|
}
|
|
67
79
|
}
|
|
68
80
|
|
|
81
|
+
function readStoredBoolean(key, fallback) {
|
|
82
|
+
try {
|
|
83
|
+
const value = globalThis.localStorage?.getItem(key);
|
|
84
|
+
|
|
85
|
+
if (value === 'true') {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (value === 'false') {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return fallback;
|
|
94
|
+
} catch {
|
|
95
|
+
return fallback;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function storeBoolean(key, value) {
|
|
100
|
+
try {
|
|
101
|
+
globalThis.localStorage?.setItem(key, String(Boolean(value)));
|
|
102
|
+
} catch {
|
|
103
|
+
// Ignore unavailable browser storage; the in-memory setting still applies.
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function readStoredString(key, fallback = '') {
|
|
108
|
+
try {
|
|
109
|
+
const value = globalThis.localStorage?.getItem(key);
|
|
110
|
+
return value === null || value === undefined ? fallback : value;
|
|
111
|
+
} catch {
|
|
112
|
+
return fallback;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function storeString(key, value) {
|
|
117
|
+
try {
|
|
118
|
+
globalThis.localStorage?.setItem(key, String(value ?? ''));
|
|
119
|
+
} catch {
|
|
120
|
+
// Ignore unavailable browser storage; the in-memory value still applies.
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function readStoredEditorActiveTab(fallback = 'messages') {
|
|
125
|
+
try {
|
|
126
|
+
const value = globalThis.localStorage?.getItem(UI_PREFERENCE_STORAGE_KEYS.sqlEditorActiveTab);
|
|
127
|
+
return EDITOR_RESULT_TABS.has(value) ? value : fallback;
|
|
128
|
+
} catch {
|
|
129
|
+
return fallback;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function storeEditorActiveTab(tab) {
|
|
134
|
+
if (!EDITOR_RESULT_TABS.has(tab)) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
globalThis.localStorage?.setItem(UI_PREFERENCE_STORAGE_KEYS.sqlEditorActiveTab, tab);
|
|
140
|
+
} catch {
|
|
141
|
+
// Ignore unavailable browser storage; the in-memory setting still applies.
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
69
145
|
function readStoredChartsHistoryTab(fallback = 'recent') {
|
|
70
146
|
try {
|
|
71
147
|
return normalizeChartsHistoryTab(globalThis.localStorage?.getItem(CHARTS_HISTORY_TAB_STORAGE_KEY) ?? fallback);
|
|
@@ -124,7 +200,7 @@ const state = {
|
|
|
124
200
|
dataBrowser: {
|
|
125
201
|
tables: [],
|
|
126
202
|
selectedTable: null,
|
|
127
|
-
tablesVisible: true,
|
|
203
|
+
tablesVisible: readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.dataTablesVisible, true),
|
|
128
204
|
table: null,
|
|
129
205
|
loading: false,
|
|
130
206
|
tableLoading: false,
|
|
@@ -135,6 +211,7 @@ const state = {
|
|
|
135
211
|
sortColumn: null,
|
|
136
212
|
sortDirection: null,
|
|
137
213
|
searchQuery: '',
|
|
214
|
+
tableSearchQuery: '',
|
|
138
215
|
searchColumn: '',
|
|
139
216
|
selectedRowIndex: null,
|
|
140
217
|
selectedRow: null,
|
|
@@ -144,10 +221,10 @@ const state = {
|
|
|
144
221
|
saveError: null,
|
|
145
222
|
},
|
|
146
223
|
editor: {
|
|
147
|
-
sqlText:
|
|
148
|
-
editorPanelVisible: true,
|
|
224
|
+
sqlText: readStoredString(UI_PREFERENCE_STORAGE_KEYS.sqlEditorQueryDraft),
|
|
225
|
+
editorPanelVisible: readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.sqlEditorEditorVisible, true),
|
|
149
226
|
history: [],
|
|
150
|
-
historyPanelVisible: true,
|
|
227
|
+
historyPanelVisible: readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.sqlEditorHistoryVisible, true),
|
|
151
228
|
historyLoading: false,
|
|
152
229
|
historyLoadingMore: false,
|
|
153
230
|
historyError: null,
|
|
@@ -163,9 +240,10 @@ const state = {
|
|
|
163
240
|
historyRuns: [],
|
|
164
241
|
historyDetailLoading: false,
|
|
165
242
|
historyDetailError: null,
|
|
166
|
-
activeTab:
|
|
243
|
+
activeTab: readStoredEditorActiveTab(),
|
|
167
244
|
executing: false,
|
|
168
245
|
result: null,
|
|
246
|
+
lastExecutedSql: '',
|
|
169
247
|
resultSortColumn: null,
|
|
170
248
|
resultSortDirection: null,
|
|
171
249
|
error: null,
|
|
@@ -181,10 +259,11 @@ const state = {
|
|
|
181
259
|
loading: false,
|
|
182
260
|
error: null,
|
|
183
261
|
historyTab: readStoredChartsHistoryTab(),
|
|
262
|
+
historyPanelVisible: readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsHistoryVisible, true),
|
|
184
263
|
selectedHistoryId: null,
|
|
185
264
|
chartHeightPreset: 'medium',
|
|
186
265
|
sqlExpanded: false,
|
|
187
|
-
resultsVisible: true,
|
|
266
|
+
resultsVisible: readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsResultsVisible, true),
|
|
188
267
|
detail: null,
|
|
189
268
|
detailLoading: false,
|
|
190
269
|
detailError: null,
|
|
@@ -196,7 +275,7 @@ const state = {
|
|
|
196
275
|
tables: [],
|
|
197
276
|
selectedTableName: null,
|
|
198
277
|
draft: null,
|
|
199
|
-
sqlPreviewVisible: true,
|
|
278
|
+
sqlPreviewVisible: readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.tableDesignerSqlPreviewVisible, true),
|
|
200
279
|
pendingImportedDraft: null,
|
|
201
280
|
loading: false,
|
|
202
281
|
detailLoading: false,
|
|
@@ -210,7 +289,8 @@ const state = {
|
|
|
210
289
|
data: null,
|
|
211
290
|
selectedName: null,
|
|
212
291
|
detail: null,
|
|
213
|
-
tablesVisible: true,
|
|
292
|
+
tablesVisible: readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.structureTablesVisible, true),
|
|
293
|
+
tableSearchQuery: '',
|
|
214
294
|
loading: false,
|
|
215
295
|
detailLoading: false,
|
|
216
296
|
error: null,
|
|
@@ -467,6 +547,10 @@ function resetDataBrowserSearch() {
|
|
|
467
547
|
state.dataBrowser.searchColumn = '';
|
|
468
548
|
}
|
|
469
549
|
|
|
550
|
+
function resetDataBrowserTableSearch() {
|
|
551
|
+
state.dataBrowser.tableSearchQuery = '';
|
|
552
|
+
}
|
|
553
|
+
|
|
470
554
|
function resetDataBrowserSort() {
|
|
471
555
|
state.dataBrowser.sortColumn = null;
|
|
472
556
|
state.dataBrowser.sortDirection = null;
|
|
@@ -647,7 +731,11 @@ function clearDataBrowserRowSelectionState() {
|
|
|
647
731
|
}
|
|
648
732
|
|
|
649
733
|
function resolveDataBrowserRowSelection(rowIndex, identity = null) {
|
|
650
|
-
const
|
|
734
|
+
const hasRowIndex =
|
|
735
|
+
rowIndex !== null &&
|
|
736
|
+
rowIndex !== undefined &&
|
|
737
|
+
(typeof rowIndex !== 'string' || rowIndex.trim() !== '');
|
|
738
|
+
const numericIndex = hasRowIndex ? Number(rowIndex) : NaN;
|
|
651
739
|
|
|
652
740
|
if (Number.isInteger(numericIndex) && numericIndex >= 0) {
|
|
653
741
|
const indexedRow = state.dataBrowser.table?.rows?.[numericIndex] ?? null;
|
|
@@ -735,10 +823,11 @@ function resetChartsState() {
|
|
|
735
823
|
state.charts.loading = false;
|
|
736
824
|
state.charts.error = null;
|
|
737
825
|
state.charts.historyTab = readStoredChartsHistoryTab(state.charts.historyTab);
|
|
826
|
+
state.charts.historyPanelVisible = readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsHistoryVisible, true);
|
|
738
827
|
state.charts.selectedHistoryId = null;
|
|
739
828
|
state.charts.chartHeightPreset = 'medium';
|
|
740
829
|
state.charts.sqlExpanded = false;
|
|
741
|
-
state.charts.resultsVisible = true;
|
|
830
|
+
state.charts.resultsVisible = readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsResultsVisible, true);
|
|
742
831
|
state.charts.detail = null;
|
|
743
832
|
state.charts.detailLoading = false;
|
|
744
833
|
state.charts.detailError = null;
|
|
@@ -890,6 +979,7 @@ function setMissingDatabaseState() {
|
|
|
890
979
|
state.dataBrowser.selectedTable = null;
|
|
891
980
|
state.dataBrowser.table = null;
|
|
892
981
|
state.dataBrowser.page = 1;
|
|
982
|
+
resetDataBrowserTableSearch();
|
|
893
983
|
resetDataBrowserSort();
|
|
894
984
|
resetDataBrowserSearch();
|
|
895
985
|
clearDataBrowserRowSelectionState();
|
|
@@ -902,7 +992,8 @@ function setMissingDatabaseState() {
|
|
|
902
992
|
state.structure.detailLoading = false;
|
|
903
993
|
state.structure.data = null;
|
|
904
994
|
state.structure.detail = null;
|
|
905
|
-
state.structure.tablesVisible = true;
|
|
995
|
+
state.structure.tablesVisible = readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.structureTablesVisible, true);
|
|
996
|
+
state.structure.tableSearchQuery = '';
|
|
906
997
|
state.structure.error = error;
|
|
907
998
|
|
|
908
999
|
state.tableDesigner.loading = false;
|
|
@@ -910,7 +1001,7 @@ function setMissingDatabaseState() {
|
|
|
910
1001
|
state.tableDesigner.tables = [];
|
|
911
1002
|
state.tableDesigner.selectedTableName = null;
|
|
912
1003
|
state.tableDesigner.draft = null;
|
|
913
|
-
state.tableDesigner.sqlPreviewVisible = true;
|
|
1004
|
+
state.tableDesigner.sqlPreviewVisible = readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.tableDesignerSqlPreviewVisible, true);
|
|
914
1005
|
state.tableDesigner.pendingImportedDraft = null;
|
|
915
1006
|
state.tableDesigner.saving = false;
|
|
916
1007
|
state.tableDesigner.searchQuery = '';
|
|
@@ -964,9 +1055,8 @@ function syncRouteContext() {
|
|
|
964
1055
|
|
|
965
1056
|
if (route.name === 'editorResults') {
|
|
966
1057
|
state.editor.activeTab = 'results';
|
|
1058
|
+
storeEditorActiveTab('results');
|
|
967
1059
|
clearQueryHistoryDetailState();
|
|
968
|
-
} else if (route.name === 'editor' && state.editor.activeTab === 'results') {
|
|
969
|
-
state.editor.activeTab = 'messages';
|
|
970
1060
|
}
|
|
971
1061
|
|
|
972
1062
|
if (route.name !== 'editorResults') {
|
|
@@ -1184,7 +1274,7 @@ async function loadChartsDetail(historyId) {
|
|
|
1184
1274
|
if (!Number.isInteger(numericId) || numericId < 1) {
|
|
1185
1275
|
state.charts.selectedHistoryId = null;
|
|
1186
1276
|
state.charts.sqlExpanded = false;
|
|
1187
|
-
state.charts.resultsVisible = true;
|
|
1277
|
+
state.charts.resultsVisible = readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsResultsVisible, true);
|
|
1188
1278
|
state.charts.detail = null;
|
|
1189
1279
|
state.charts.detailLoading = false;
|
|
1190
1280
|
state.charts.detailError = null;
|
|
@@ -1197,7 +1287,7 @@ async function loadChartsDetail(historyId) {
|
|
|
1197
1287
|
|
|
1198
1288
|
state.charts.selectedHistoryId = numericId;
|
|
1199
1289
|
state.charts.sqlExpanded = false;
|
|
1200
|
-
state.charts.resultsVisible = true;
|
|
1290
|
+
state.charts.resultsVisible = readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsResultsVisible, true);
|
|
1201
1291
|
state.charts.detail = null;
|
|
1202
1292
|
state.charts.detailLoading = true;
|
|
1203
1293
|
state.charts.detailError = null;
|
|
@@ -1859,6 +1949,7 @@ function invalidateDatabaseCaches() {
|
|
|
1859
1949
|
state.dataBrowser.selectedTable = null;
|
|
1860
1950
|
state.dataBrowser.table = null;
|
|
1861
1951
|
state.dataBrowser.page = 1;
|
|
1952
|
+
resetDataBrowserTableSearch();
|
|
1862
1953
|
resetDataBrowserSearch();
|
|
1863
1954
|
clearDataBrowserRowSelectionState();
|
|
1864
1955
|
state.dataBrowser.pendingOpenRow = null;
|
|
@@ -1868,7 +1959,7 @@ function invalidateDatabaseCaches() {
|
|
|
1868
1959
|
state.tableDesigner.tables = [];
|
|
1869
1960
|
state.tableDesigner.selectedTableName = null;
|
|
1870
1961
|
state.tableDesigner.draft = null;
|
|
1871
|
-
state.tableDesigner.sqlPreviewVisible = true;
|
|
1962
|
+
state.tableDesigner.sqlPreviewVisible = readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.tableDesignerSqlPreviewVisible, true);
|
|
1872
1963
|
state.tableDesigner.pendingImportedDraft = null;
|
|
1873
1964
|
state.tableDesigner.saving = false;
|
|
1874
1965
|
state.tableDesigner.searchQuery = '';
|
|
@@ -1878,7 +1969,8 @@ function invalidateDatabaseCaches() {
|
|
|
1878
1969
|
resetChartsState();
|
|
1879
1970
|
state.structure.data = null;
|
|
1880
1971
|
state.structure.detail = null;
|
|
1881
|
-
state.structure.tablesVisible = true;
|
|
1972
|
+
state.structure.tablesVisible = readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.structureTablesVisible, true);
|
|
1973
|
+
state.structure.tableSearchQuery = '';
|
|
1882
1974
|
state.mediaTagging.loading = false;
|
|
1883
1975
|
state.mediaTagging.previewLoading = false;
|
|
1884
1976
|
state.mediaTagging.saving = false;
|
|
@@ -2296,6 +2388,92 @@ export function closeModal() {
|
|
|
2296
2388
|
closeModalInternal();
|
|
2297
2389
|
}
|
|
2298
2390
|
|
|
2391
|
+
export async function openDataRowUpdatePreview(rowIndex, values, identity = null) {
|
|
2392
|
+
const tableName = state.dataBrowser.selectedTable;
|
|
2393
|
+
const selected = resolveDataBrowserRowSelection(rowIndex, identity);
|
|
2394
|
+
|
|
2395
|
+
if (!tableName || !selected.identity) {
|
|
2396
|
+
pushToast('The selected row could not be loaded.', 'alert');
|
|
2397
|
+
return null;
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
state.dataBrowser.saving = true;
|
|
2401
|
+
state.dataBrowser.saveError = null;
|
|
2402
|
+
emitChange();
|
|
2403
|
+
|
|
2404
|
+
try {
|
|
2405
|
+
const response = await api.previewDataTableRowUpdate(tableName, {
|
|
2406
|
+
identity: selected.identity,
|
|
2407
|
+
values,
|
|
2408
|
+
});
|
|
2409
|
+
|
|
2410
|
+
state.modal = {
|
|
2411
|
+
kind: 'row-update-preview',
|
|
2412
|
+
target: 'data',
|
|
2413
|
+
tableName,
|
|
2414
|
+
rowIndex: selected.rowIndex,
|
|
2415
|
+
identity: selected.identity,
|
|
2416
|
+
values,
|
|
2417
|
+
preview: response.data,
|
|
2418
|
+
error: null,
|
|
2419
|
+
submitting: false,
|
|
2420
|
+
};
|
|
2421
|
+
emitChange();
|
|
2422
|
+
return response.data;
|
|
2423
|
+
} catch (error) {
|
|
2424
|
+
state.dataBrowser.saveError = normalizeError(error);
|
|
2425
|
+
emitChange();
|
|
2426
|
+
return null;
|
|
2427
|
+
} finally {
|
|
2428
|
+
state.dataBrowser.saving = false;
|
|
2429
|
+
emitChange();
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
export async function openEditorRowUpdatePreview(rowIndex, values) {
|
|
2434
|
+
const numericIndex = Number(rowIndex);
|
|
2435
|
+
const result = state.editor.result;
|
|
2436
|
+
const row = result?.rows?.[numericIndex];
|
|
2437
|
+
const tableName = result?.editing?.tableName ?? null;
|
|
2438
|
+
|
|
2439
|
+
if (!tableName || !row || !canEditQueryResult()) {
|
|
2440
|
+
pushToast('The selected query result row could not be loaded.', 'alert');
|
|
2441
|
+
return null;
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
state.editor.saving = true;
|
|
2445
|
+
state.editor.saveError = null;
|
|
2446
|
+
emitChange();
|
|
2447
|
+
|
|
2448
|
+
try {
|
|
2449
|
+
const response = await api.previewDataTableRowUpdate(tableName, {
|
|
2450
|
+
identity: row.__identity,
|
|
2451
|
+
values,
|
|
2452
|
+
});
|
|
2453
|
+
|
|
2454
|
+
state.modal = {
|
|
2455
|
+
kind: 'row-update-preview',
|
|
2456
|
+
target: 'editor',
|
|
2457
|
+
tableName,
|
|
2458
|
+
rowIndex: numericIndex,
|
|
2459
|
+
identity: row.__identity,
|
|
2460
|
+
values,
|
|
2461
|
+
preview: response.data,
|
|
2462
|
+
error: null,
|
|
2463
|
+
submitting: false,
|
|
2464
|
+
};
|
|
2465
|
+
emitChange();
|
|
2466
|
+
return response.data;
|
|
2467
|
+
} catch (error) {
|
|
2468
|
+
state.editor.saveError = normalizeError(error);
|
|
2469
|
+
emitChange();
|
|
2470
|
+
return null;
|
|
2471
|
+
} finally {
|
|
2472
|
+
state.editor.saving = false;
|
|
2473
|
+
emitChange();
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2299
2477
|
export function toggleChartsSqlPanel() {
|
|
2300
2478
|
state.charts.sqlExpanded = !state.charts.sqlExpanded;
|
|
2301
2479
|
emitChange();
|
|
@@ -2303,6 +2481,19 @@ export function toggleChartsSqlPanel() {
|
|
|
2303
2481
|
|
|
2304
2482
|
export function toggleChartsResultsPanel() {
|
|
2305
2483
|
state.charts.resultsVisible = !state.charts.resultsVisible;
|
|
2484
|
+
storeBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsResultsVisible, state.charts.resultsVisible);
|
|
2485
|
+
emitChange();
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
export function setChartsHistoryPanelVisibility(visible) {
|
|
2489
|
+
const nextValue = typeof visible === 'boolean' ? visible : !Boolean(state.charts.historyPanelVisible);
|
|
2490
|
+
|
|
2491
|
+
if (state.charts.historyPanelVisible === nextValue) {
|
|
2492
|
+
return;
|
|
2493
|
+
}
|
|
2494
|
+
|
|
2495
|
+
state.charts.historyPanelVisible = nextValue;
|
|
2496
|
+
storeBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsHistoryVisible, nextValue);
|
|
2306
2497
|
emitChange();
|
|
2307
2498
|
}
|
|
2308
2499
|
|
|
@@ -2550,6 +2741,7 @@ export function setCurrentQuery(query) {
|
|
|
2550
2741
|
const nextLineCount = Math.max(1, nextQuery.split('\n').length);
|
|
2551
2742
|
|
|
2552
2743
|
state.editor.sqlText = nextQuery;
|
|
2744
|
+
storeString(UI_PREFERENCE_STORAGE_KEYS.sqlEditorQueryDraft, nextQuery);
|
|
2553
2745
|
|
|
2554
2746
|
if (previousLineCount !== nextLineCount) {
|
|
2555
2747
|
emitChange();
|
|
@@ -2558,7 +2750,9 @@ export function setCurrentQuery(query) {
|
|
|
2558
2750
|
|
|
2559
2751
|
export function clearCurrentQuery() {
|
|
2560
2752
|
state.editor.sqlText = '';
|
|
2753
|
+
storeString(UI_PREFERENCE_STORAGE_KEYS.sqlEditorQueryDraft, '');
|
|
2561
2754
|
state.editor.result = null;
|
|
2755
|
+
state.editor.lastExecutedSql = '';
|
|
2562
2756
|
resetEditorResultSort();
|
|
2563
2757
|
state.editor.error = null;
|
|
2564
2758
|
clearQueryHistoryDetailState();
|
|
@@ -2566,22 +2760,17 @@ export function clearCurrentQuery() {
|
|
|
2566
2760
|
state.editor.saving = false;
|
|
2567
2761
|
state.editor.deleting = false;
|
|
2568
2762
|
state.editor.saveError = null;
|
|
2569
|
-
if (state.editor.activeTab === 'results' || state.editor.activeTab === 'performance') {
|
|
2570
|
-
state.editor.activeTab = 'messages';
|
|
2571
|
-
}
|
|
2572
2763
|
emitChange();
|
|
2573
2764
|
}
|
|
2574
2765
|
|
|
2575
2766
|
export function clearEditorResults() {
|
|
2576
2767
|
state.editor.result = null;
|
|
2768
|
+
state.editor.lastExecutedSql = '';
|
|
2577
2769
|
resetEditorResultSort();
|
|
2578
2770
|
state.editor.error = null;
|
|
2579
2771
|
state.editor.selectedRowIndex = null;
|
|
2580
2772
|
state.editor.saving = false;
|
|
2581
2773
|
state.editor.saveError = null;
|
|
2582
|
-
if (state.editor.activeTab === 'results') {
|
|
2583
|
-
state.editor.activeTab = 'messages';
|
|
2584
|
-
}
|
|
2585
2774
|
emitChange();
|
|
2586
2775
|
}
|
|
2587
2776
|
|
|
@@ -2593,16 +2782,23 @@ export function setEditorPanelVisibility(visible) {
|
|
|
2593
2782
|
}
|
|
2594
2783
|
|
|
2595
2784
|
state.editor.editorPanelVisible = nextValue;
|
|
2785
|
+
storeBoolean(UI_PREFERENCE_STORAGE_KEYS.sqlEditorEditorVisible, nextValue);
|
|
2596
2786
|
emitChange();
|
|
2597
2787
|
}
|
|
2598
2788
|
|
|
2599
2789
|
export function setEditorTab(tab) {
|
|
2790
|
+
if (!EDITOR_RESULT_TABS.has(tab)) {
|
|
2791
|
+
return;
|
|
2792
|
+
}
|
|
2793
|
+
|
|
2600
2794
|
state.editor.activeTab = tab;
|
|
2795
|
+
storeEditorActiveTab(tab);
|
|
2601
2796
|
emitChange();
|
|
2602
2797
|
}
|
|
2603
2798
|
|
|
2604
2799
|
export async function executeCurrentQuery() {
|
|
2605
2800
|
state.editor.executing = true;
|
|
2801
|
+
state.editor.lastExecutedSql = state.editor.sqlText;
|
|
2606
2802
|
state.editor.error = null;
|
|
2607
2803
|
state.editor.selectedRowIndex = null;
|
|
2608
2804
|
state.editor.saving = false;
|
|
@@ -2614,7 +2810,6 @@ export async function executeCurrentQuery() {
|
|
|
2614
2810
|
state.editor.result = response.data;
|
|
2615
2811
|
resetEditorResultSort();
|
|
2616
2812
|
state.editor.error = null;
|
|
2617
|
-
state.editor.activeTab = 'results';
|
|
2618
2813
|
invalidateDatabaseCaches();
|
|
2619
2814
|
await refreshQueryHistoryState();
|
|
2620
2815
|
pushToast(response.message || `Executed ${response.data.statementCount} SQL statement(s).`, 'success');
|
|
@@ -2622,6 +2817,7 @@ export async function executeCurrentQuery() {
|
|
|
2622
2817
|
} catch (error) {
|
|
2623
2818
|
state.editor.error = normalizeError(error);
|
|
2624
2819
|
state.editor.activeTab = 'messages';
|
|
2820
|
+
storeEditorActiveTab('messages');
|
|
2625
2821
|
await refreshQueryHistoryState();
|
|
2626
2822
|
return false;
|
|
2627
2823
|
} finally {
|
|
@@ -2676,6 +2872,7 @@ export function setQueryHistoryPanelVisibility(visible) {
|
|
|
2676
2872
|
}
|
|
2677
2873
|
|
|
2678
2874
|
state.editor.historyPanelVisible = nextValue;
|
|
2875
|
+
storeBoolean(UI_PREFERENCE_STORAGE_KEYS.sqlEditorHistoryVisible, nextValue);
|
|
2679
2876
|
emitChange();
|
|
2680
2877
|
}
|
|
2681
2878
|
|
|
@@ -2744,6 +2941,7 @@ export function openQueryHistoryInEditor(historyId, options = {}) {
|
|
|
2744
2941
|
setActiveQueryHistoryItem(historyId);
|
|
2745
2942
|
clearQueryHistoryDetailState();
|
|
2746
2943
|
state.editor.sqlText = options.append ? [state.editor.sqlText.trim(), rawSql].filter(Boolean).join('\n\n') : rawSql;
|
|
2944
|
+
storeString(UI_PREFERENCE_STORAGE_KEYS.sqlEditorQueryDraft, state.editor.sqlText);
|
|
2747
2945
|
emitChange();
|
|
2748
2946
|
return true;
|
|
2749
2947
|
}
|
|
@@ -2842,6 +3040,7 @@ export async function selectStructureEntry(name) {
|
|
|
2842
3040
|
|
|
2843
3041
|
export function toggleStructureTablesPanel() {
|
|
2844
3042
|
state.structure.tablesVisible = state.structure.tablesVisible === false;
|
|
3043
|
+
storeBoolean(UI_PREFERENCE_STORAGE_KEYS.structureTablesVisible, state.structure.tablesVisible);
|
|
2845
3044
|
emitChange();
|
|
2846
3045
|
}
|
|
2847
3046
|
|
|
@@ -2850,6 +3049,16 @@ export function setTableDesignerSearchQuery(query) {
|
|
|
2850
3049
|
emitChange();
|
|
2851
3050
|
}
|
|
2852
3051
|
|
|
3052
|
+
export function setDataTableSearchQuery(query) {
|
|
3053
|
+
state.dataBrowser.tableSearchQuery = String(query ?? '');
|
|
3054
|
+
emitChange();
|
|
3055
|
+
}
|
|
3056
|
+
|
|
3057
|
+
export function setStructureTableSearchQuery(query) {
|
|
3058
|
+
state.structure.tableSearchQuery = String(query ?? '');
|
|
3059
|
+
emitChange();
|
|
3060
|
+
}
|
|
3061
|
+
|
|
2853
3062
|
export function setTableDesignerSqlPreviewVisibility(visible) {
|
|
2854
3063
|
const nextValue = typeof visible === 'boolean' ? visible : !Boolean(state.tableDesigner.sqlPreviewVisible);
|
|
2855
3064
|
|
|
@@ -2858,6 +3067,7 @@ export function setTableDesignerSqlPreviewVisibility(visible) {
|
|
|
2858
3067
|
}
|
|
2859
3068
|
|
|
2860
3069
|
state.tableDesigner.sqlPreviewVisible = nextValue;
|
|
3070
|
+
storeBoolean(UI_PREFERENCE_STORAGE_KEYS.tableDesignerSqlPreviewVisible, nextValue);
|
|
2861
3071
|
emitChange();
|
|
2862
3072
|
}
|
|
2863
3073
|
|
|
@@ -3433,6 +3643,7 @@ export function setDataSearchColumn(columnName) {
|
|
|
3433
3643
|
|
|
3434
3644
|
export function toggleDataTablesPanel() {
|
|
3435
3645
|
state.dataBrowser.tablesVisible = state.dataBrowser.tablesVisible === false;
|
|
3646
|
+
storeBoolean(UI_PREFERENCE_STORAGE_KEYS.dataTablesVisible, state.dataBrowser.tablesVisible);
|
|
3436
3647
|
emitChange();
|
|
3437
3648
|
}
|
|
3438
3649
|
|
|
@@ -3499,9 +3710,10 @@ export async function setDataPageSize(pageSize) {
|
|
|
3499
3710
|
}
|
|
3500
3711
|
}
|
|
3501
3712
|
|
|
3502
|
-
export async function submitDataRowUpdate(rowIndex, values, identity = null) {
|
|
3713
|
+
export async function submitDataRowUpdate(rowIndex, values, identity = null, options = {}) {
|
|
3503
3714
|
const tableName = state.dataBrowser.selectedTable;
|
|
3504
3715
|
const selected = resolveDataBrowserRowSelection(rowIndex, identity);
|
|
3716
|
+
const reportErrorToModal = Boolean(options.reportErrorToModal);
|
|
3505
3717
|
|
|
3506
3718
|
if (!tableName || !selected.identity) {
|
|
3507
3719
|
pushToast('The selected row could not be loaded.', 'alert');
|
|
@@ -3523,8 +3735,12 @@ export async function submitDataRowUpdate(rowIndex, values, identity = null) {
|
|
|
3523
3735
|
clearDataBrowserRowSelectionState();
|
|
3524
3736
|
return response.data;
|
|
3525
3737
|
} catch (error) {
|
|
3526
|
-
|
|
3527
|
-
|
|
3738
|
+
if (reportErrorToModal) {
|
|
3739
|
+
withModalError(error);
|
|
3740
|
+
} else {
|
|
3741
|
+
state.dataBrowser.saveError = normalizeError(error);
|
|
3742
|
+
emitChange();
|
|
3743
|
+
}
|
|
3528
3744
|
return null;
|
|
3529
3745
|
} finally {
|
|
3530
3746
|
state.dataBrowser.saving = false;
|
|
@@ -3576,11 +3792,12 @@ export async function submitDataRowDelete(rowIndex, options = {}) {
|
|
|
3576
3792
|
}
|
|
3577
3793
|
}
|
|
3578
3794
|
|
|
3579
|
-
export async function submitEditorRowUpdate(rowIndex, values) {
|
|
3795
|
+
export async function submitEditorRowUpdate(rowIndex, values, options = {}) {
|
|
3580
3796
|
const numericIndex = Number(rowIndex);
|
|
3581
3797
|
const result = state.editor.result;
|
|
3582
3798
|
const row = result?.rows?.[numericIndex];
|
|
3583
3799
|
const tableName = result?.editing?.tableName ?? null;
|
|
3800
|
+
const reportErrorToModal = Boolean(options.reportErrorToModal);
|
|
3584
3801
|
|
|
3585
3802
|
if (!tableName || !row || !canEditQueryResult()) {
|
|
3586
3803
|
pushToast('The selected query result row could not be loaded.', 'alert');
|
|
@@ -3610,8 +3827,12 @@ export async function submitEditorRowUpdate(rowIndex, values) {
|
|
|
3610
3827
|
emitChange();
|
|
3611
3828
|
return response.data;
|
|
3612
3829
|
} catch (error) {
|
|
3613
|
-
|
|
3614
|
-
|
|
3830
|
+
if (reportErrorToModal) {
|
|
3831
|
+
withModalError(error);
|
|
3832
|
+
} else {
|
|
3833
|
+
state.editor.saveError = normalizeError(error);
|
|
3834
|
+
emitChange();
|
|
3835
|
+
}
|
|
3615
3836
|
return null;
|
|
3616
3837
|
} finally {
|
|
3617
3838
|
state.editor.saving = false;
|
|
@@ -3689,6 +3910,29 @@ export async function submitDeleteRowConfirmation() {
|
|
|
3689
3910
|
return result;
|
|
3690
3911
|
}
|
|
3691
3912
|
|
|
3913
|
+
export async function submitRowUpdatePreviewConfirmation() {
|
|
3914
|
+
const modal = state.modal;
|
|
3915
|
+
|
|
3916
|
+
if (modal?.kind !== 'row-update-preview') {
|
|
3917
|
+
return null;
|
|
3918
|
+
}
|
|
3919
|
+
|
|
3920
|
+
startModalSubmission();
|
|
3921
|
+
|
|
3922
|
+
const result =
|
|
3923
|
+
modal.target === 'editor'
|
|
3924
|
+
? await submitEditorRowUpdate(modal.rowIndex, modal.values, { reportErrorToModal: true })
|
|
3925
|
+
: await submitDataRowUpdate(modal.rowIndex, modal.values, modal.identity, {
|
|
3926
|
+
reportErrorToModal: true,
|
|
3927
|
+
});
|
|
3928
|
+
|
|
3929
|
+
if (result) {
|
|
3930
|
+
closeModalInternal();
|
|
3931
|
+
}
|
|
3932
|
+
|
|
3933
|
+
return result;
|
|
3934
|
+
}
|
|
3935
|
+
|
|
3692
3936
|
export async function saveCurrentQueryChartDraft() {
|
|
3693
3937
|
if (state.modal?.kind !== 'chart-editor' || !state.modal.draft) {
|
|
3694
3938
|
return null;
|
|
@@ -3896,6 +4140,18 @@ export function getCurrentConnection(snapshot = state) {
|
|
|
3896
4140
|
}
|
|
3897
4141
|
|
|
3898
4142
|
export function getQueryMessages(snapshot = state) {
|
|
4143
|
+
const queryText = snapshot.editor.result?.sql ?? (snapshot.editor.error ? snapshot.editor.lastExecutedSql : '');
|
|
4144
|
+
const queryMessages = queryText
|
|
4145
|
+
? [
|
|
4146
|
+
{
|
|
4147
|
+
tone: 'muted',
|
|
4148
|
+
label: 'QUERY',
|
|
4149
|
+
value: queryText,
|
|
4150
|
+
kind: 'query',
|
|
4151
|
+
},
|
|
4152
|
+
]
|
|
4153
|
+
: [];
|
|
4154
|
+
|
|
3899
4155
|
if (snapshot.editor.error) {
|
|
3900
4156
|
return [
|
|
3901
4157
|
{
|
|
@@ -3903,6 +4159,7 @@ export function getQueryMessages(snapshot = state) {
|
|
|
3903
4159
|
label: snapshot.editor.error.code,
|
|
3904
4160
|
value: snapshot.editor.error.message,
|
|
3905
4161
|
},
|
|
4162
|
+
...queryMessages,
|
|
3906
4163
|
];
|
|
3907
4164
|
}
|
|
3908
4165
|
|
|
@@ -3916,14 +4173,17 @@ export function getQueryMessages(snapshot = state) {
|
|
|
3916
4173
|
];
|
|
3917
4174
|
}
|
|
3918
4175
|
|
|
3919
|
-
return
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
4176
|
+
return [
|
|
4177
|
+
...snapshot.editor.result.statements.map(statement => ({
|
|
4178
|
+
tone: statement.kind === 'resultSet' ? 'success' : inferStatusTone(statement.keyword),
|
|
4179
|
+
label: `${statement.keyword} #${statement.index + 1}`,
|
|
4180
|
+
value:
|
|
4181
|
+
statement.kind === 'resultSet'
|
|
4182
|
+
? `${statement.rowCount} row(s) returned.`
|
|
4183
|
+
: `${statement.changes} row(s) affected.`,
|
|
4184
|
+
})),
|
|
4185
|
+
...queryMessages,
|
|
4186
|
+
];
|
|
3927
4187
|
}
|
|
3928
4188
|
|
|
3929
4189
|
export function getQueryPerformance(snapshot = state) {
|
|
@@ -402,7 +402,7 @@ function renderChartCard(chart, state, analysis) {
|
|
|
402
402
|
export function renderChartsDetail(state) {
|
|
403
403
|
const detail = state.charts.detail;
|
|
404
404
|
const selectedHistoryId = state.charts.selectedHistoryId;
|
|
405
|
-
const historyVisible = state.
|
|
405
|
+
const historyVisible = state.charts.historyPanelVisible !== false;
|
|
406
406
|
|
|
407
407
|
if (!selectedHistoryId) {
|
|
408
408
|
return renderEmptyChartDetail();
|
|
@@ -514,7 +514,7 @@ export function renderChartsDetail(state) {
|
|
|
514
514
|
}
|
|
515
515
|
|
|
516
516
|
export function renderChartsView(state) {
|
|
517
|
-
const historyVisible = state.
|
|
517
|
+
const historyVisible = state.charts.historyPanelVisible !== false || !state.charts.selectedHistoryId;
|
|
518
518
|
|
|
519
519
|
if (!state.connections.active && state.charts.error?.code === 'ACTIVE_DATABASE_REQUIRED') {
|
|
520
520
|
return {
|