sqlite-hub 1.0.0 → 1.1.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 (40) hide show
  1. package/README.md +47 -33
  2. package/bin/sqlite-hub.js +127 -47
  3. package/frontend/js/api.js +6 -0
  4. package/frontend/js/app.js +199 -34
  5. package/frontend/js/components/modal.js +17 -1
  6. package/frontend/js/components/queryChartRenderer.js +28 -3
  7. package/frontend/js/components/queryEditor.js +20 -24
  8. package/frontend/js/components/queryHistoryDetail.js +1 -1
  9. package/frontend/js/components/queryHistoryHeader.js +48 -0
  10. package/frontend/js/components/queryHistoryList.js +132 -0
  11. package/frontend/js/components/queryHistoryPanel.js +72 -136
  12. package/frontend/js/components/structureGraph.js +699 -89
  13. package/frontend/js/components/tableDesignerEditor.js +3 -5
  14. package/frontend/js/store.js +73 -7
  15. package/frontend/js/utils/exportFilenames.js +3 -1
  16. package/frontend/js/views/charts.js +320 -169
  17. package/frontend/js/views/connections.js +59 -64
  18. package/frontend/js/views/data.js +12 -20
  19. package/frontend/js/views/editor.js +0 -2
  20. package/frontend/js/views/settings.js +78 -0
  21. package/frontend/js/views/structure.js +27 -13
  22. package/frontend/styles/components.css +155 -0
  23. package/frontend/styles/structure-graph.css +140 -35
  24. package/frontend/styles/tailwind.generated.css +1 -1
  25. package/frontend/styles/views.css +12 -6
  26. package/package.json +3 -2
  27. package/server/routes/export.js +89 -22
  28. package/server/routes/externalApi.js +84 -2
  29. package/server/routes/settings.js +37 -28
  30. package/server/services/appInfoService.js +215 -0
  31. package/server/services/databaseCommandService.js +50 -6
  32. package/server/services/sqlite/exportService.js +307 -22
  33. package/tests/api-token-auth.test.js +110 -1
  34. package/tests/cli-args.test.js +16 -3
  35. package/tests/database-command-service.test.js +39 -2
  36. package/tests/export-blob.test.js +54 -1
  37. package/tests/export-filenames.test.js +4 -0
  38. package/tests/settings-api-tokens-route.test.js +22 -1
  39. package/tests/settings-metadata.test.js +99 -1
  40. package/tests/settings-view.test.js +28 -0
@@ -351,11 +351,9 @@ export function renderTableDesignerEditor(state) {
351
351
  value="${escapeHtml(draft.tableName)}"
352
352
  />
353
353
  ${
354
- draft.mode === "edit"
355
- ? `<div class="status-badge status-badge--muted">FROM ${escapeHtml(
356
- draft.originalTableName
357
- )}</div>`
358
- : `<div class="status-badge status-badge--primary">CREATE</div>`
354
+ draft.mode === "create"
355
+ ? `<div class="status-badge status-badge--primary">CREATE</div>`
356
+ : ""
359
357
  }
360
358
  </div>
361
359
  <div class="table-designer-main__subtitle">
@@ -49,6 +49,7 @@ const UI_PREFERENCE_STORAGE_KEYS = {
49
49
  dataTablesVisible: 'sqlite_hub_data_tables_visible',
50
50
  structureTablesVisible: 'sqlite_hub_structure_tables_visible',
51
51
  chartsHistoryVisible: 'sqlite_hub_charts_history_visible',
52
+ chartsQueryVisible: 'sqlite_hub_charts_query_visible',
52
53
  chartsResultsVisible: 'sqlite_hub_charts_results_visible',
53
54
  tableDesignerSqlPreviewVisible: 'sqlite_hub_table_designer_sql_preview_visible',
54
55
  documentsEditorVisible: 'sqlite_hub_documents_editor_visible',
@@ -63,6 +64,8 @@ const TEXT_EXPORT_FORMAT_LABELS = {
63
64
  csv: 'CSV',
64
65
  tsv: 'TSV',
65
66
  md: 'Markdown',
67
+ json: 'JSON',
68
+ parquet: 'Parquet',
66
69
  };
67
70
  const MISSING_DATABASE_ERROR = {
68
71
  code: 'ACTIVE_DATABASE_REQUIRED',
@@ -262,6 +265,9 @@ const state = {
262
265
  error: null,
263
266
  appVersion: null,
264
267
  sqliteVersion: null,
268
+ versionCheck: null,
269
+ versionCheckLoading: false,
270
+ versionCheckError: null,
265
271
  },
266
272
  overview: {
267
273
  data: null,
@@ -331,10 +337,13 @@ const state = {
331
337
  loading: false,
332
338
  error: null,
333
339
  historyTab: readStoredChartsHistoryTab(),
340
+ historySearchInput: '',
341
+ historySearch: '',
334
342
  historyPanelVisible: readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsHistoryVisible, true),
343
+ detailPanelVisible: false,
335
344
  selectedHistoryId: null,
336
345
  chartHeightPreset: 'medium',
337
- sqlExpanded: false,
346
+ queryVisible: readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsQueryVisible, true),
338
347
  resultsVisible: readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsResultsVisible, true),
339
348
  detail: null,
340
349
  detailLoading: false,
@@ -929,10 +938,13 @@ function resetChartsState() {
929
938
  state.charts.loading = false;
930
939
  state.charts.error = null;
931
940
  state.charts.historyTab = readStoredChartsHistoryTab(state.charts.historyTab);
941
+ state.charts.historySearchInput = '';
942
+ state.charts.historySearch = '';
932
943
  state.charts.historyPanelVisible = readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsHistoryVisible, true);
944
+ state.charts.detailPanelVisible = false;
933
945
  state.charts.selectedHistoryId = null;
934
946
  state.charts.chartHeightPreset = 'medium';
935
- state.charts.sqlExpanded = false;
947
+ state.charts.queryVisible = readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsQueryVisible, true);
936
948
  state.charts.resultsVisible = readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsResultsVisible, true);
937
949
  state.charts.detail = null;
938
950
  state.charts.detailLoading = false;
@@ -1019,7 +1031,7 @@ function normalizeChartsHeightPreset(value) {
1019
1031
  }
1020
1032
 
1021
1033
  function normalizeChartsHistoryTab(value) {
1022
- return ['recent', 'saved'].includes(value) ? value : 'recent';
1034
+ return ['recent', 'saved', 'unsaved'].includes(value) ? value : 'recent';
1023
1035
  }
1024
1036
 
1025
1037
  function mergeQueryHistoryItemWithChartSummary(updatedItem, fallbackItem = null) {
@@ -1358,6 +1370,33 @@ export function setSettingsSection(section) {
1358
1370
  emitChange();
1359
1371
  }
1360
1372
 
1373
+ export async function checkSettingsAppVersion() {
1374
+ state.settings.versionCheckLoading = true;
1375
+ state.settings.versionCheckError = null;
1376
+ emitChange();
1377
+
1378
+ try {
1379
+ const response = await api.checkAppVersion();
1380
+ const result = response.data ?? null;
1381
+
1382
+ state.settings.versionCheck = result;
1383
+ pushToast(
1384
+ result?.updateAvailable
1385
+ ? `SQLite Hub v${result.latestVersion} is available.`
1386
+ : 'SQLite Hub is up to date.',
1387
+ 'success',
1388
+ );
1389
+ return result;
1390
+ } catch (error) {
1391
+ state.settings.versionCheckError = normalizeError(error);
1392
+ pushToast('Version check failed.', 'alert');
1393
+ return null;
1394
+ } finally {
1395
+ state.settings.versionCheckLoading = false;
1396
+ emitChange();
1397
+ }
1398
+ }
1399
+
1361
1400
  export async function deleteSettingsApiToken(tokenId) {
1362
1401
  state.settings.tokenSaving = true;
1363
1402
  state.settings.error = null;
@@ -1557,7 +1596,8 @@ async function loadChartsDetail(historyId) {
1557
1596
 
1558
1597
  if (!Number.isInteger(numericId) || numericId < 1) {
1559
1598
  state.charts.selectedHistoryId = null;
1560
- state.charts.sqlExpanded = false;
1599
+ state.charts.detailPanelVisible = false;
1600
+ state.charts.queryVisible = readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsQueryVisible, true);
1561
1601
  state.charts.resultsVisible = readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsResultsVisible, true);
1562
1602
  state.charts.detail = null;
1563
1603
  state.charts.detailLoading = false;
@@ -1570,7 +1610,7 @@ async function loadChartsDetail(historyId) {
1570
1610
  }
1571
1611
 
1572
1612
  state.charts.selectedHistoryId = numericId;
1573
- state.charts.sqlExpanded = false;
1613
+ state.charts.queryVisible = readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsQueryVisible, true);
1574
1614
  state.charts.resultsVisible = readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsResultsVisible, true);
1575
1615
  state.charts.detail = null;
1576
1616
  state.charts.detailLoading = true;
@@ -3448,7 +3488,8 @@ export async function openEditorRowUpdatePreview(rowIndex, values) {
3448
3488
  }
3449
3489
 
3450
3490
  export function toggleChartsSqlPanel() {
3451
- state.charts.sqlExpanded = !state.charts.sqlExpanded;
3491
+ state.charts.queryVisible = !state.charts.queryVisible;
3492
+ storeBoolean(UI_PREFERENCE_STORAGE_KEYS.chartsQueryVisible, state.charts.queryVisible);
3452
3493
  emitChange();
3453
3494
  }
3454
3495
 
@@ -3470,6 +3511,17 @@ export function setChartsHistoryPanelVisibility(visible) {
3470
3511
  emitChange();
3471
3512
  }
3472
3513
 
3514
+ export function setChartsDetailPanelVisibility(visible) {
3515
+ const nextValue = typeof visible === 'boolean' ? visible : !Boolean(state.charts.detailPanelVisible);
3516
+
3517
+ if (state.charts.detailPanelVisible === nextValue) {
3518
+ return;
3519
+ }
3520
+
3521
+ state.charts.detailPanelVisible = nextValue;
3522
+ emitChange();
3523
+ }
3524
+
3473
3525
  export function setChartsHeightPreset(preset) {
3474
3526
  const nextPreset = normalizeChartsHeightPreset(preset);
3475
3527
 
@@ -3497,6 +3549,18 @@ export function setChartsHistoryTab(tab) {
3497
3549
  emitChange();
3498
3550
  }
3499
3551
 
3552
+ export function setChartsHistorySearchInput(query) {
3553
+ const nextQuery = String(query ?? '');
3554
+
3555
+ if (state.charts.historySearchInput === nextQuery && state.charts.historySearch === nextQuery.trim()) {
3556
+ return;
3557
+ }
3558
+
3559
+ state.charts.historySearchInput = nextQuery;
3560
+ state.charts.historySearch = nextQuery.trim();
3561
+ emitChange();
3562
+ }
3563
+
3500
3564
  export function updateCurrentQueryChartDraftField(field, value) {
3501
3565
  if (state.modal?.kind !== 'chart-editor' || !state.modal.draft) {
3502
3566
  return;
@@ -3935,7 +3999,9 @@ export function openQueryHistoryInEditor(historyId, options = {}) {
3935
3999
  clearQueryHistoryDetailState();
3936
4000
  state.editor.sqlText = options.append ? [state.editor.sqlText.trim(), rawSql].filter(Boolean).join('\n\n') : rawSql;
3937
4001
  storeString(UI_PREFERENCE_STORAGE_KEYS.sqlEditorQueryDraft, state.editor.sqlText);
3938
- emitChange();
4002
+ if (options.notify !== false) {
4003
+ emitChange();
4004
+ }
3939
4005
  return true;
3940
4006
  }
3941
4007
 
@@ -2,6 +2,8 @@ export const TEXT_EXPORT_EXTENSIONS = {
2
2
  csv: "csv",
3
3
  tsv: "tsv",
4
4
  md: "md",
5
+ json: "json",
6
+ parquet: "parquet",
5
7
  };
6
8
 
7
9
  export function normalizeTextExportFormat(format = "csv") {
@@ -10,7 +12,7 @@ export function normalizeTextExportFormat(format = "csv") {
10
12
  }
11
13
 
12
14
  function stripKnownTextExportExtension(value = "") {
13
- return String(value).replace(/\.(csv|tsv|md)$/i, "");
15
+ return String(value).replace(/\.(csv|tsv|md|json|parquet)$/i, "");
14
16
  }
15
17
 
16
18
  export function sanitizeExportFilenameBase(value, fallback = "export") {