sqlite-hub 1.1.1 → 1.1.2

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 (48) hide show
  1. package/README.md +17 -196
  2. package/bin/sqlite-hub.js +7 -2
  3. package/frontend/js/api.js +60 -0
  4. package/frontend/js/app.js +110 -0
  5. package/frontend/js/components/dropdownButton.js +92 -0
  6. package/frontend/js/components/modal.js +991 -876
  7. package/frontend/js/components/queryEditor.js +24 -15
  8. package/frontend/js/components/queryHistoryDetail.js +20 -1
  9. package/frontend/js/components/queryHistoryHeader.js +3 -2
  10. package/frontend/js/components/rowEditorPanel.js +1 -0
  11. package/frontend/js/components/sidebar.js +1 -0
  12. package/frontend/js/router.js +2 -0
  13. package/frontend/js/store.js +383 -37
  14. package/frontend/js/utils/riskySql.js +165 -0
  15. package/frontend/js/views/backups.js +176 -0
  16. package/frontend/js/views/charts.js +1 -1
  17. package/frontend/js/views/documents.js +184 -154
  18. package/frontend/js/views/structure.js +26 -24
  19. package/frontend/styles/components.css +128 -0
  20. package/frontend/styles/tailwind.generated.css +1 -1
  21. package/frontend/styles/views.css +33 -21
  22. package/package.json +2 -1
  23. package/server/routes/backups.js +120 -0
  24. package/server/routes/connections.js +26 -3
  25. package/server/routes/externalApi.js +6 -2
  26. package/server/server.js +3 -1
  27. package/server/services/databaseCommandService.js +4 -3
  28. package/server/services/sqlite/backupService.js +497 -66
  29. package/server/services/sqlite/connectionManager.js +2 -2
  30. package/server/services/sqlite/importService.js +25 -0
  31. package/server/services/sqlite/sqlExecutor.js +2 -0
  32. package/server/services/storage/appStateStore.js +379 -88
  33. package/tests/api-token-auth.test.js +45 -2
  34. package/tests/backup-manager.test.js +140 -0
  35. package/tests/backups-view.test.js +64 -0
  36. package/tests/charts-height-preset-storage.test.js +60 -0
  37. package/tests/charts-route-state.test.js +144 -0
  38. package/tests/cli-service-delegation.test.js +97 -0
  39. package/tests/connection-removal.test.js +52 -0
  40. package/tests/database-command-service.test.js +37 -2
  41. package/tests/documents-view.test.js +132 -0
  42. package/tests/dropdown-button.test.js +75 -0
  43. package/tests/query-editor.test.js +28 -0
  44. package/tests/query-history-detail.test.js +37 -0
  45. package/tests/query-history-header.test.js +30 -0
  46. package/tests/risky-sql.test.js +30 -0
  47. package/tests/row-editor-null-values.test.js +24 -0
  48. package/tests/structure-view.test.js +56 -0
@@ -1,3 +1,4 @@
1
+ import { renderDropdownButton } from '../components/dropdownButton.js';
1
2
  import { escapeHtml, formatCompactDateTime, formatNumber } from '../utils/format.js';
2
3
  import { renderMarkdownPreview } from '../utils/markdownDocuments.js';
3
4
 
@@ -100,164 +101,191 @@ function renderDocumentsSidebar(documents) {
100
101
  `;
101
102
  }
102
103
 
103
- function renderDocumentToolbar(documents) {
104
+ function renderDocumentImportFileInput() {
105
+ return `
106
+ <input
107
+ accept=".md,.markdown,text/markdown,text/plain"
108
+ data-bind="document-import-file"
109
+ hidden
110
+ type="file"
111
+ />
112
+ `;
113
+ }
114
+
115
+ function renderNewDocumentDropdown(documents, className = '') {
116
+ return `
117
+ <div class="${escapeHtml(className)}">
118
+ ${renderDropdownButton({
119
+ disabled: documents.saving,
120
+ icon: 'add',
121
+ label: 'New Document',
122
+ title: 'New document',
123
+ items: [
124
+ {
125
+ action: 'create-document',
126
+ icon: 'draft',
127
+ label: 'Blank Page',
128
+ },
129
+ {
130
+ action: 'import-document-markdown',
131
+ icon: 'upload_file',
132
+ label: 'Import .md',
133
+ },
134
+ ],
135
+ })}
136
+ </div>
137
+ `;
138
+ }
139
+
140
+ function renderDocumentsPanelToggle(documents) {
141
+ const visible = documents.documentsVisible !== false;
142
+
143
+ return `
144
+ <button
145
+ class="standard-button panel-toggle-button ${visible ? '' : 'is-active'}"
146
+ aria-pressed="${visible ? 'false' : 'true'}"
147
+ data-action="toggle-documents-panel"
148
+ type="button"
149
+ >
150
+ <span class="material-symbols-outlined">${visible ? 'visibility_off' : 'visibility'}</span>
151
+ ${visible ? 'Hide Documents' : 'Show Documents'}
152
+ </button>
153
+ `;
154
+ }
155
+
156
+ function renderDocumentPaneToggle(documents, pane) {
157
+ const isEditor = pane === 'editor';
158
+ const visible = isEditor ? documents.editorVisible : documents.previewVisible;
159
+ const label = `${visible ? 'Hide' : 'Show'} ${isEditor ? 'Editor' : 'Preview'}`;
160
+ const icon = visible ? 'visibility_off' : isEditor ? 'edit_note' : 'visibility';
161
+
162
+ return `
163
+ <button
164
+ class="standard-button panel-toggle-button documents-pane__toggle ${visible ? '' : 'is-active'}"
165
+ aria-pressed="${visible ? 'false' : 'true'}"
166
+ data-action="toggle-document-pane"
167
+ data-pane="${escapeHtml(pane)}"
168
+ type="button"
169
+ >
170
+ <span class="material-symbols-outlined">${icon}</span>
171
+ ${label}
172
+ </button>
173
+ `;
174
+ }
175
+
176
+ function renderDocumentsTitlebar(documents, options = {}) {
177
+ const { showDocumentActions = false, showFilename = true } = options;
104
178
  const disabled = documents.saving || documents.detailLoading || !documents.selectedId;
105
179
 
106
180
  return `
107
181
  <div class="documents-titlebar">
108
- <label class="documents-filename-field">
109
- <input
110
- class="control-input"
111
- data-bind="document-field"
112
- data-field="filename"
113
- name="filename"
114
- spellcheck="false"
115
- type="text"
116
- value="${escapeHtml(documents.draftFilename)}"
117
- ${!documents.selectedId ? 'disabled' : ''}
118
- />
119
- </label>
120
- <button
121
- class="signature-button documents-create-button"
122
- data-action="create-document"
123
- type="button"
124
- ${documents.saving ? 'disabled aria-disabled="true"' : ''}
125
- >
126
- <span class="material-symbols-outlined">add</span>
127
- New Document
128
- </button>
129
- </div>
130
- <div class="documents-toolbar">
131
-
132
-
133
-
134
- <div class="documents-toolbar__actions">
135
- <button
136
- class="standard-button panel-toggle-button ${documents.editorVisible ? '' : 'is-active'}"
137
- aria-pressed="${documents.editorVisible ? 'false' : 'true'}"
138
- data-action="toggle-document-pane"
139
- data-pane="editor"
140
- type="button"
141
- >
142
- <span class="material-symbols-outlined">${documents.editorVisible ? 'visibility_off' : 'edit_note'}</span>
143
- ${documents.editorVisible ? 'Hide Editor' : 'Show Editor'}
144
- </button>
145
- <button
146
- class="standard-button panel-toggle-button ${documents.previewVisible ? '' : 'is-active'}"
147
- aria-pressed="${documents.previewVisible ? 'false' : 'true'}"
148
- data-action="toggle-document-pane"
149
- data-pane="preview"
150
- type="button"
151
- >
152
- <span class="material-symbols-outlined">${documents.previewVisible ? 'visibility_off' : 'visibility'}</span>
153
- ${documents.previewVisible ? 'Hide Preview' : 'Show Preview'}
154
- </button>
155
- <input
156
- accept=".md,.markdown,text/markdown,text/plain"
157
- data-bind="document-import-file"
158
- hidden
159
- type="file"
160
- />
161
- </div>
162
- <div class="documents-toolbar__actions">
163
- <button
164
- class="standard-button"
165
- data-action="open-document-insert-table-modal"
166
- type="button"
167
- ${disabled ? 'disabled aria-disabled="true"' : ''}
168
- >
169
- <span class="material-symbols-outlined">table_chart</span>
170
- Insert Table
171
- </button>
172
- <button
173
- class="standard-button"
174
- data-action="open-document-insert-note-modal"
175
- type="button"
176
- ${disabled ? 'disabled aria-disabled="true"' : ''}
177
- >
178
- <span class="material-symbols-outlined">note_add</span>
179
- Insert Note
180
- </button>
181
- <button
182
- class="standard-button"
183
- data-action="export-document-markdown"
184
- type="button"
185
- ${disabled ? 'disabled aria-disabled="true"' : ''}
186
- >
187
- <span class="material-symbols-outlined">download</span>
188
- Export .md
189
- </button>
190
- <button
191
- class="standard-button"
192
- data-action="import-document-markdown"
193
- type="button"
194
- ${documents.saving ? 'disabled aria-disabled="true"' : ''}
195
- >
196
- <span class="material-symbols-outlined">upload_file</span>
197
- Import .md
198
- </button>
199
- </div>
200
- <div class="documents-toolbar__actions">
201
- <button
202
- class="standard-button"
203
- data-action="delete-document"
204
- type="button"
205
- ${disabled || documents.deleting ? 'disabled aria-disabled="true"' : ''}
206
- >
207
- <span class="material-symbols-outlined">delete</span>
208
- Delete
209
- </button>
210
- <button
211
- class="signature-button"
212
- data-action="save-document"
213
- type="button"
214
- ${disabled || !documents.dirty ? 'disabled aria-disabled="true"' : ''}
215
- >
216
- <span class="material-symbols-outlined">save</span>
217
- ${documents.saving ? 'Saving...' : 'Save'}
218
- </button>
219
- </div>
182
+ ${renderDocumentsPanelToggle(documents)}
183
+ ${
184
+ showFilename
185
+ ? `
186
+ <label class="documents-filename-field">
187
+ <input
188
+ class="control-input"
189
+ data-bind="document-field"
190
+ data-field="filename"
191
+ name="filename"
192
+ spellcheck="false"
193
+ type="text"
194
+ value="${escapeHtml(documents.draftFilename)}"
195
+ ${!documents.selectedId ? 'disabled' : ''}
196
+ />
197
+ </label>
198
+ `
199
+ : ''
200
+ }
201
+ ${renderNewDocumentDropdown(documents, 'documents-create-button')}
202
+ ${
203
+ showDocumentActions
204
+ ? `
205
+ ${renderDropdownButton({
206
+ disabled,
207
+ icon: 'add_box',
208
+ label: 'Insert',
209
+ title: 'Insert content',
210
+ items: [
211
+ {
212
+ action: 'open-document-insert-table-modal',
213
+ icon: 'table_chart',
214
+ label: 'Insert Table',
215
+ },
216
+ {
217
+ action: 'open-document-insert-note-modal',
218
+ icon: 'note_add',
219
+ label: 'Insert Note',
220
+ },
221
+ ],
222
+ })}
223
+ <button
224
+ class="standard-button"
225
+ data-action="export-document-markdown"
226
+ type="button"
227
+ ${disabled ? 'disabled aria-disabled="true"' : ''}
228
+ >
229
+ <span class="material-symbols-outlined">download</span>
230
+ Export .md
231
+ </button>
232
+ <button
233
+ class="delete-button"
234
+ data-action="delete-document"
235
+ type="button"
236
+ ${disabled || documents.deleting ? 'disabled aria-disabled="true"' : ''}
237
+ >
238
+ <span class="material-symbols-outlined">delete</span>
239
+ Delete
240
+ </button>
241
+ `
242
+ : ''
243
+ }
220
244
  </div>
221
245
  `;
222
246
  }
223
247
 
224
248
  function renderDocumentEditor(documents) {
225
- if (!documents.editorVisible) {
226
- return '';
227
- }
228
-
229
249
  return `
230
- <section class="documents-pane documents-pane--editor">
250
+ <section class="documents-pane documents-pane--editor ${documents.editorVisible ? '' : 'documents-pane--collapsed'}">
231
251
  <div class="documents-pane__header">
232
- <span>Editor</span>
233
- <span>${formatNumber(documents.draftContent.length)} chars</span>
252
+ ${renderDocumentPaneToggle(documents, 'editor')}
253
+ <span class="documents-pane__meta">${formatNumber(documents.draftContent.length)} chars</span>
234
254
  </div>
235
- <textarea
236
- class="documents-editor-input custom-scrollbar"
237
- data-bind="document-field"
238
- data-field="content"
239
- name="content"
240
- spellcheck="true"
241
- ${!documents.selectedId ? 'disabled' : ''}
242
- >${escapeHtml(documents.draftContent)}</textarea>
255
+ ${
256
+ documents.editorVisible
257
+ ? `
258
+ <textarea
259
+ class="documents-editor-input custom-scrollbar"
260
+ data-bind="document-field"
261
+ data-field="content"
262
+ name="content"
263
+ spellcheck="true"
264
+ ${!documents.selectedId ? 'disabled' : ''}
265
+ >${escapeHtml(documents.draftContent)}</textarea>
266
+ `
267
+ : ''
268
+ }
243
269
  </section>
244
270
  `;
245
271
  }
246
272
 
247
273
  function renderDocumentPreview(documents) {
248
- if (!documents.previewVisible) {
249
- return '';
250
- }
251
-
252
274
  return `
253
- <section class="documents-pane documents-pane--preview">
275
+ <section class="documents-pane documents-pane--preview ${documents.previewVisible ? '' : 'documents-pane--collapsed'}">
254
276
  <div class="documents-pane__header">
255
- <span>Preview</span>
256
- <span>${escapeHtml(documents.dirty ? 'unsaved' : 'saved')}</span>
257
- </div>
258
- <div class="document-markdown-preview custom-scrollbar" data-document-preview>
259
- ${renderMarkdownPreview(documents.draftContent)}
277
+ ${renderDocumentPaneToggle(documents, 'preview')}
278
+ <span class="documents-pane__meta">${escapeHtml(documents.dirty ? 'unsaved' : 'saved')}</span>
260
279
  </div>
280
+ ${
281
+ documents.previewVisible
282
+ ? `
283
+ <div class="document-markdown-preview custom-scrollbar" data-document-preview>
284
+ ${renderMarkdownPreview(documents.draftContent)}
285
+ </div>
286
+ `
287
+ : ''
288
+ }
261
289
  </section>
262
290
  `;
263
291
  }
@@ -269,15 +297,7 @@ function renderEmptyDocumentsState(documents) {
269
297
  <p class="font-headline text-2xl font-black uppercase tracking-tight text-primary-container">
270
298
  No Documents
271
299
  </p>
272
- <button
273
- class="signature-button mt-4"
274
- data-action="create-document"
275
- type="button"
276
- ${documents.saving ? 'disabled aria-disabled="true"' : ''}
277
- >
278
- <span class="material-symbols-outlined">add</span>
279
- New Document
280
- </button>
300
+ ${renderNewDocumentDropdown(documents, 'mt-4')}
281
301
  </div>
282
302
  `;
283
303
  }
@@ -286,6 +306,7 @@ function renderDocumentDetail(documents) {
286
306
  if (documents.loading && !documents.items.length) {
287
307
  return `
288
308
  <main class="documents-view__detail">
309
+ ${renderDocumentsTitlebar(documents, { showFilename: false })}
289
310
  <div class="documents-empty-state">
290
311
  <span class="material-symbols-outlined">sync</span>
291
312
  <p class="font-headline text-2xl font-black uppercase tracking-tight text-primary-container">
@@ -299,18 +320,26 @@ function renderDocumentDetail(documents) {
299
320
  if (!documents.selectedId) {
300
321
  return `
301
322
  <main class="documents-view__detail">
323
+ ${renderDocumentsTitlebar(documents, { showFilename: false })}
302
324
  ${renderEmptyDocumentsState(documents)}
303
325
  </main>
304
326
  `;
305
327
  }
306
328
 
307
- const paneCount = Number(documents.editorVisible) + Number(documents.previewVisible);
329
+ const workspaceClasses = [
330
+ 'documents-workspace',
331
+ documents.editorVisible && documents.previewVisible ? 'documents-workspace--split' : '',
332
+ !documents.editorVisible ? 'documents-workspace--editor-collapsed' : '',
333
+ !documents.previewVisible ? 'documents-workspace--preview-collapsed' : '',
334
+ ]
335
+ .filter(Boolean)
336
+ .join(' ');
308
337
 
309
338
  return `
310
339
  <main class="documents-view__detail">
311
- ${renderDocumentToolbar(documents)}
340
+ ${renderDocumentsTitlebar(documents, { showDocumentActions: true })}
312
341
  ${documents.saveError ? `<div class="documents-error">${escapeHtml(documents.saveError.message)}</div>` : ''}
313
- <div class="documents-workspace ${paneCount > 1 ? 'documents-workspace--split' : ''}">
342
+ <div class="${workspaceClasses}">
314
343
  ${renderDocumentEditor(documents)}
315
344
  ${renderDocumentPreview(documents)}
316
345
  </div>
@@ -327,13 +356,14 @@ export function renderDocumentsView(state) {
327
356
  }
328
357
 
329
358
  const documents = state.documents;
359
+ const documentsVisible = documents.documentsVisible !== false;
330
360
 
331
361
  return {
332
- main: `<div class="documents-view">
333
- ${renderDocumentsSidebar(documents)}
362
+ main: `<div class="documents-view ${documentsVisible ? 'documents-view--with-subnavi' : ''}">
363
+ ${renderDocumentImportFileInput()}
364
+ ${documentsVisible ? renderDocumentsSidebar(documents) : ''}
334
365
  ${renderDocumentDetail(documents)}
335
- </div>
336
- </div>`,
366
+ </div>`,
337
367
  panel: '',
338
368
  };
339
369
  }
@@ -1,4 +1,5 @@
1
1
  import { clearInspector, renderDdlSection, renderInspector } from '../components/structureGraph.js';
2
+ import { renderDropdownButton } from '../components/dropdownButton.js';
2
3
  import { escapeHtml, formatNumber } from '../utils/format.js';
3
4
 
4
5
  function renderEntryGroup(title, entries, activeName, options = {}) {
@@ -230,30 +231,31 @@ function renderGraphSurface(structure, selectedName, detail, detailLoading, tabl
230
231
  </button>
231
232
  </div>
232
233
  <div class="structure-graph__toolbar-actions">
233
- <button
234
- class="${toolbarButtonClass}"
235
- data-structure-graph-action="fit"
236
- type="button"
237
- >
238
- <span class="material-symbols-outlined text-sm">fit_screen</span>
239
- Fit Graph
240
- </button>
241
- <button
242
- class="${toolbarButtonClass}"
243
- data-structure-graph-action="relayout"
244
- type="button"
245
- >
246
- <span class="material-symbols-outlined text-sm">device_hub</span>
247
- Recalculate Layout
248
- </button>
249
- <button
250
- class="${toolbarButtonClass}"
251
- data-structure-graph-action="clear"
252
- type="button"
253
- >
254
- <span class="material-symbols-outlined text-sm">close</span>
255
- Clear Selection
256
- </button>
234
+ ${renderDropdownButton({
235
+ icon: 'format_shapes',
236
+ label: 'Format',
237
+ title: 'Format graph',
238
+ items: [
239
+ {
240
+ action: 'fit',
241
+ actionAttribute: 'data-structure-graph-action',
242
+ icon: 'fit_screen',
243
+ label: 'Fit Graph',
244
+ },
245
+ {
246
+ action: 'relayout',
247
+ actionAttribute: 'data-structure-graph-action',
248
+ icon: 'device_hub',
249
+ label: 'Recalculate Layout',
250
+ },
251
+ {
252
+ action: 'clear',
253
+ actionAttribute: 'data-structure-graph-action',
254
+ icon: 'close',
255
+ label: 'Clear Selection',
256
+ },
257
+ ],
258
+ })}
257
259
  <button
258
260
  class="${toolbarButtonClass}"
259
261
  data-structure-graph-action="open-data"
@@ -477,6 +477,7 @@
477
477
  .signature-button,
478
478
  .delete-button,
479
479
  .toolbar-button,
480
+ .dropdown-button__toggle,
480
481
  .query-history-icon-button,
481
482
  .top-nav-icon {
482
483
  align-items: center;
@@ -532,10 +533,29 @@
532
533
  text-transform: uppercase;
533
534
  }
534
535
 
536
+ .backup-safety-actions {
537
+ align-items: center;
538
+ display: flex;
539
+ flex-wrap: nowrap;
540
+ gap: var(--spacing-3);
541
+ overflow-x: auto;
542
+ }
543
+
544
+ .backup-safety-actions > button {
545
+ flex: 0 0 auto;
546
+ white-space: nowrap;
547
+ }
548
+
549
+ .backup-safety-actions > .standard-button {
550
+ margin-right: auto;
551
+ }
552
+
535
553
  .standard-button:focus-visible,
536
554
  .signature-button:focus-visible,
537
555
  .delete-button:focus-visible,
538
556
  .toolbar-button:focus-visible,
557
+ .dropdown-button__toggle:focus-visible,
558
+ .dropdown-button__item:focus-visible,
539
559
  .query-result-column-menu__toggle:focus-visible,
540
560
  .query-result-column-menu__item:focus-visible,
541
561
  .query-history-icon-button:focus-visible,
@@ -566,6 +586,94 @@
566
586
  color var(--transition-fast);
567
587
  }
568
588
 
589
+ .dropdown-button {
590
+ display: inline-flex;
591
+ position: relative;
592
+ }
593
+
594
+ .dropdown-button__toggle {
595
+ cursor: pointer;
596
+ gap: var(--spacing-2);
597
+ list-style: none;
598
+ }
599
+
600
+ .dropdown-button__toggle::-webkit-details-marker {
601
+ display: none;
602
+ }
603
+
604
+ .dropdown-button[open] .dropdown-button__toggle {
605
+ background: var(--primary-alpha-12);
606
+ border-color: rgb(var(--rgb-primary) / 0.38);
607
+ color: var(--color-primary-container);
608
+ }
609
+
610
+ .dropdown-button__chevron {
611
+ font-size: 18px;
612
+ line-height: 1;
613
+ transition: transform var(--transition-fast);
614
+ }
615
+
616
+ .dropdown-button[open] .dropdown-button__chevron {
617
+ transform: rotate(180deg);
618
+ }
619
+
620
+ .dropdown-button__panel {
621
+ background: var(--color-surface-container);
622
+ border: 1px solid rgb(var(--rgb-outline) / 0.22);
623
+ box-shadow: 0 18px 40px rgb(0 0 0 / 0.35);
624
+ min-width: 13rem;
625
+ padding: var(--spacing-1);
626
+ position: absolute;
627
+ top: calc(100% + var(--spacing-2));
628
+ z-index: 35;
629
+ }
630
+
631
+ .dropdown-button--align-right .dropdown-button__panel {
632
+ right: 0;
633
+ }
634
+
635
+ .dropdown-button--align-left .dropdown-button__panel {
636
+ left: 0;
637
+ }
638
+
639
+ .dropdown-button__item {
640
+ align-items: center;
641
+ color: var(--color-on-surface);
642
+ cursor: pointer;
643
+ display: flex;
644
+ font-family: var(--font-family-body);
645
+ font-size: 0.75rem;
646
+ gap: var(--spacing-2);
647
+ justify-content: flex-start;
648
+ line-height: 1.3;
649
+ padding: 0.6rem 0.75rem;
650
+ text-align: left;
651
+ transition:
652
+ background-color var(--transition-fast),
653
+ color var(--transition-fast);
654
+ width: 100%;
655
+ }
656
+
657
+ .dropdown-button__item:hover {
658
+ background: var(--color-surface-highest);
659
+ color: var(--color-primary-container);
660
+ }
661
+
662
+ .dropdown-button__item:disabled,
663
+ .dropdown-button__item[aria-disabled='true'] {
664
+ color: rgb(var(--rgb-on-surface-variant) / 0.32);
665
+ cursor: not-allowed;
666
+ }
667
+
668
+ .dropdown-button__item--danger {
669
+ color: var(--color-error);
670
+ }
671
+
672
+ .dropdown-button__item .material-symbols-outlined {
673
+ font-size: 18px;
674
+ line-height: 1;
675
+ }
676
+
569
677
  .query-result-header-cell {
570
678
  min-width: 10rem;
571
679
  position: relative;
@@ -890,6 +998,26 @@
890
998
  text-transform: uppercase;
891
999
  }
892
1000
 
1001
+ .query-history-search {
1002
+ display: block;
1003
+ margin-top: var(--spacing-4);
1004
+ position: relative;
1005
+ }
1006
+
1007
+ .query-history-search__icon {
1008
+ color: rgb(var(--rgb-on-surface-variant) / 0.55);
1009
+ font-size: 1rem;
1010
+ left: var(--spacing-3);
1011
+ pointer-events: none;
1012
+ position: absolute;
1013
+ top: 50%;
1014
+ transform: translateY(-50%);
1015
+ }
1016
+
1017
+ .query-history-search__input {
1018
+ padding-left: calc(var(--spacing-3) + 1rem + var(--spacing-2));
1019
+ }
1020
+
893
1021
  .query-history-list-scroll {
894
1022
  flex: 1;
895
1023
  min-height: 0;