sqlite-hub 0.8.7 → 0.9.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.
Files changed (32) hide show
  1. package/changelog.md +11 -0
  2. package/frontend/index.html +1 -1
  3. package/frontend/js/api.js +67 -0
  4. package/frontend/js/app.js +1398 -999
  5. package/frontend/js/components/modal.js +208 -3
  6. package/frontend/js/components/queryEditor.js +9 -4
  7. package/frontend/js/components/queryHistoryDetail.js +20 -10
  8. package/frontend/js/components/queryHistoryPanel.js +4 -3
  9. package/frontend/js/components/rowEditorPanel.js +26 -5
  10. package/frontend/js/components/sidebar.js +49 -5
  11. package/frontend/js/components/structureGraph.js +614 -632
  12. package/frontend/js/components/tableDesignerSqlPreview.js +24 -25
  13. package/frontend/js/lib/mediaTaggingDefaults.js +27 -0
  14. package/frontend/js/router.js +81 -71
  15. package/frontend/js/store.js +3091 -2184
  16. package/frontend/js/views/charts.js +2 -2
  17. package/frontend/js/views/data.js +28 -14
  18. package/frontend/js/views/editor.js +172 -177
  19. package/frontend/js/views/mediaTagging.js +861 -0
  20. package/frontend/styles/base.css +11 -1
  21. package/frontend/styles/components.css +48 -6
  22. package/frontend/styles/views.css +796 -0
  23. package/package.json +1 -1
  24. package/server/routes/charts.js +4 -1
  25. package/server/routes/data.js +14 -0
  26. package/server/routes/mediaTagging.js +166 -0
  27. package/server/routes/sql.js +1 -0
  28. package/server/server.js +4 -0
  29. package/server/services/sqlite/dataBrowserService.js +25 -0
  30. package/server/services/sqlite/exportService.js +31 -1
  31. package/server/services/sqlite/mediaTaggingService.js +1689 -0
  32. package/server/services/storage/appStateStore.js +321 -2
@@ -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 Query History
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 saved queries in the SQL Editor first. They will appear here automatically.
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
- state.dataBrowser.selectedRowIndex === rowIndex
346
- ? 'bg-surface-bright'
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 || typeof rowIndex !== 'number') {
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: [{ name: 'rowIndex', value: String(rowIndex) }],
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: column.name,
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 "../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 {
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
- return `
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
- const items = getQueryMessages(state);
25
+ const items = getQueryMessages(state);
31
26
 
32
- return `
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
- .map(
37
- (item) => `
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
- item.tone === "alert" ? "text-error" : "text-primary-container"
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
- .join("")}
41
+ `,
42
+ )
43
+ .join('')}
49
44
  </div>
50
45
  </div>
51
46
  `;
52
47
  }
53
48
 
54
49
  function renderPerformancePane(state) {
55
- const metrics = getQueryPerformance(state);
50
+ const metrics = getQueryPerformance(state);
56
51
 
57
- return `
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
- String(metrics.timingMs ?? 0)
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
- formatNumber(metrics.statementCount)
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
- formatNumber(metrics.rowCount)
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
- formatNumber(metrics.affectedRowCount)
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
- const editing = state.editor.result?.editing;
87
+ const editing = state.editor.result?.editing;
93
88
 
94
- if (!editing) {
95
- return {
96
- enabled: false,
97
- message: "",
98
- };
99
- }
89
+ if (!editing) {
90
+ return {
91
+ enabled: false,
92
+ message: '',
93
+ };
94
+ }
100
95
 
101
- if (state.connections.active?.readOnly) {
102
- return {
103
- enabled: false,
104
- message: "The active database is opened read-only, so query result editing is disabled.",
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
- enabled: false,
111
- message: editing.reason || "Only direct single-table SELECT results can be edited here.",
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
- const uniqueColumns = [];
123
- const seen = new Set();
117
+ const uniqueColumns = [];
118
+ const seen = new Set();
124
119
 
125
- columns.forEach((column) => {
126
- if (!column?.sourceColumn || seen.has(column.sourceColumn)) {
127
- return;
128
- }
120
+ columns.forEach(column => {
121
+ if (!column?.sourceColumn || seen.has(column.sourceColumn)) {
122
+ return;
123
+ }
129
124
 
130
- seen.add(column.sourceColumn);
131
- uniqueColumns.push(column);
132
- });
125
+ seen.add(column.sourceColumn);
126
+ uniqueColumns.push(column);
127
+ });
133
128
 
134
- return uniqueColumns;
129
+ return uniqueColumns;
135
130
  }
136
131
 
137
132
  function renderEditorRowPanel(state) {
138
- const result = state.editor.result;
139
- const rowIndex = state.editor.selectedRowIndex;
140
- const row = typeof rowIndex === "number" ? result?.rows?.[rowIndex] ?? null : null;
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
- if (!result || !row || typeof rowIndex !== "number") {
143
- return "";
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 value = row[column.resultName];
153
- if (isBlobPreview(value) || (value && typeof value === "object")) {
154
- return false;
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
- return true;
158
- });
159
- const readonlyColumns = uniqueColumns.filter((column) => {
160
- if (!column.visible) {
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
- if (column.identity || column.generated) {
165
- return true;
166
- }
152
+ return true;
153
+ });
154
+ const readonlyColumns = uniqueColumns.filter(column => {
155
+ if (!column.visible) {
156
+ return false;
157
+ }
167
158
 
168
- const value = row[column.resultName];
169
- return isBlobPreview(value) || (value && typeof value === "object");
170
- });
171
- const editingState = getResultEditingState(state);
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
- return renderRowEditorPanel({
174
- title: result.editing?.tableName ?? "Query Result",
175
- sectionLabel: "Row Editor",
176
- subtitle: `query row ${rowIndex + 1}`,
177
- closeAction: "clear-editor-row-selection",
178
- formName: "save-editor-row",
179
- hiddenFields: [{ name: "rowIndex", value: String(rowIndex) }],
180
- disabledMessage: editingState.enabled ? "" : editingState.message,
181
- editableFields: editableColumns.map((column) => {
182
- const value = row[column.resultName];
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
- return {
185
- name: column.sourceColumn,
186
- label: column.sourceColumn,
187
- value: value === null || value === undefined ? "" : String(value),
188
- };
189
- }),
190
- readonlyFields: readonlyColumns.map((column) => ({
191
- name: column.sourceColumn,
192
- label: column.sourceColumn,
193
- value: formatCellValue(row[column.resultName]),
194
- })),
195
- saveError: state.editor.saveError,
196
- saving: state.editor.saving,
197
- deleting: state.editor.deleting,
198
- deleteAction: "delete-editor-row",
199
- deleteRowIndex: rowIndex,
200
- deleteEnabled: editingState.enabled && Boolean(row.__identity),
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
- const activeTab = state.editor.activeTab;
206
- const counts = {
207
- resultRows: state.editor.result?.rows?.length ?? 0,
208
- messages: getQueryMessages(state).length,
209
- statementCount: state.editor.result?.statementCount ?? 0,
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
- let content = renderMessagesPane(state);
207
+ let content = renderMessagesPane(state);
213
208
 
214
- if (activeTab === "performance") {
215
- content = renderPerformancePane(state);
216
- } else if (activeTab === "results") {
217
- const editingState = getResultEditingState(state);
209
+ if (activeTab === 'performance') {
210
+ content = renderPerformancePane(state);
211
+ } else if (activeTab === 'results') {
212
+ const editingState = getResultEditingState(state);
218
213
 
219
- content = state.connections.active
220
- ? renderQueryResultsPane(state.editor.result, {
221
- selectedRowIndex: state.editor.selectedRowIndex,
222
- editable: editingState.enabled,
223
- sortColumn: state.editor.resultSortColumn,
224
- sortDirection: state.editor.resultSortDirection,
225
- })
226
- : renderMissingDatabase();
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
- return `
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
- const connection = getCurrentConnection(state);
239
- const editorSectionClass = state.editor.editorPanelVisible ? "min-h-[27.5%]" : "flex-none";
240
- const resultsSectionClass = "flex-1";
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
- return {
243
- main: `
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
- isResultsRoute ? "border-b-4 border-background" : ""
242
+ isResultsRoute ? 'border-b-4 border-background' : ''
248
243
  }">
249
244
  ${renderQueryEditor({
250
- query: state.editor.sqlText,
251
- executing: state.editor.executing,
252
- exporting: state.editor.exportLoading,
253
- historyLoading: state.editor.historyLoading,
254
- historyTotal: state.editor.historyTotal,
255
- editorVisible: state.editor.editorPanelVisible,
256
- historyVisible: state.editor.historyPanelVisible,
257
- title: connection?.label ?? "SQLite Query Workspace",
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
- state.editor.historyPanelVisible
266
- ? renderQueryHistoryPanel({
267
- items: state.editor.history,
268
- loading: state.editor.historyLoading,
269
- loadingMore: state.editor.historyLoadingMore,
270
- error: state.editor.historyError,
271
- activeTab: state.editor.historyTab,
272
- search: state.editor.historySearchInput,
273
- total: state.editor.historyTotal,
274
- hasMore: state.editor.historyHasMore,
275
- activeHistoryId: state.editor.historyActiveId,
276
- selectedHistoryId: state.editor.historySelectedId,
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
- panel:
283
- isResultsRoute && state.editor.selectedRowIndex !== null
284
- ? renderEditorRowPanel(state)
285
- : state.editor.historySelectedId
286
- ? renderQueryHistoryDetail({
287
- item: state.editor.historyDetail,
288
- runs: state.editor.historyRuns,
289
- loading: state.editor.historyDetailLoading,
290
- error: state.editor.historyDetailError,
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
  }