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 "./dropdownButton.js";
1
2
  import { escapeHtml, highlightSql } from "../utils/format.js";
2
3
 
3
4
  function renderLineNumbers(query) {
@@ -57,21 +58,29 @@ export function renderQueryEditor({
57
58
  `;
58
59
 
59
60
  const center = `
60
- <button
61
- class="${secondaryButtonClass}"
62
- data-action="format-current-query"
63
- type="button"
64
- >
65
- <span class="material-symbols-outlined text-sm">format_align_left</span>
66
- Format
67
- </button>
68
- <button
69
- class="${secondaryButtonClass}"
70
- data-action="clear-query"
71
- type="button"
72
- >
73
- Clear
74
- </button>
61
+ ${renderDropdownButton({
62
+ icon: "edit_note",
63
+ label: "Editor",
64
+ title: "Editor actions",
65
+ items: [
66
+ {
67
+ action: "format-current-query",
68
+ icon: "format_align_left",
69
+ label: "Format",
70
+ },
71
+ {
72
+ action: "clear-query",
73
+ icon: "delete_sweep",
74
+ label: "Clear",
75
+ danger: true,
76
+ },
77
+ {
78
+ action: "copy-current-query",
79
+ icon: "content_copy",
80
+ label: "Copy to Clipboard",
81
+ },
82
+ ],
83
+ })}
75
84
  <button
76
85
  class="${secondaryButtonClass}"
77
86
  data-action="open-query-export-modal"
@@ -35,11 +35,30 @@ function getQueryTypeTone(queryType) {
35
35
  return "muted";
36
36
  }
37
37
 
38
+ function getExecutionSourceTone(source) {
39
+ if (source === "api") {
40
+ return "primary";
41
+ }
42
+
43
+ if (source === "cli") {
44
+ return "warning";
45
+ }
46
+
47
+ if (source === "mcp") {
48
+ return "muted";
49
+ }
50
+
51
+ return "success";
52
+ }
53
+
38
54
  function renderRunItem(run) {
55
+ const executedBy = String(run.executedBy ?? "user").trim().toLowerCase() || "user";
56
+
39
57
  return `
40
58
  <div class="border border-outline-variant/10 bg-surface-container px-3 py-3">
41
59
  <div class="flex flex-wrap items-center gap-2">
42
60
  ${renderStatusBadge(run.status, run.status === "error" ? "alert" : "success")}
61
+ ${renderStatusBadge(executedBy, getExecutionSourceTone(executedBy))}
43
62
  <span class="text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
44
63
  ${escapeHtml(formatCompactDateTime(run.executedAt))}
45
64
  </span>
@@ -199,7 +218,7 @@ export function renderQueryHistoryDetail({
199
218
  "</button>",
200
219
  '<button class="delete-button" data-action="open-delete-query-history-modal" data-history-id="',
201
220
  escapeHtml(item.id),
202
- '" type="button">Delete</button></div>',
221
+ '" type="button"><span class="material-symbols-outlined text-sm">delete</span>Delete</button></div>',
203
222
  '<div class="mt-6"><div class="text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">SQL</div>',
204
223
  '<pre class="query-history-detail-sql custom-scrollbar mt-2 overflow-auto border border-outline-variant/10 bg-surface-container-lowest p-4 font-mono text-sm leading-6 text-on-surface"><code>',
205
224
  highlightSql(item.rawSql),
@@ -34,10 +34,11 @@ export function renderQueryHistorySearch({
34
34
  placeholder = 'Search SQL, titles, notes...',
35
35
  } = {}) {
36
36
  return `
37
- <label class="mt-4 block">
37
+ <label class="query-history-search">
38
38
  <span class="sr-only">Search query history</span>
39
+ <span class="material-symbols-outlined query-history-search__icon" aria-hidden="true">search</span>
39
40
  <input
40
- class="control-input w-full border border-outline-variant/20 bg-surface-container text-sm text-on-surface outline-none placeholder:text-on-surface-variant/35 focus:border-primary-container"
41
+ class="control-input query-history-search__input w-full border border-outline-variant/20 bg-surface-container text-sm text-on-surface outline-none placeholder:text-on-surface-variant/35 focus:border-primary-container"
41
42
  data-bind="${escapeHtml(bind)}"
42
43
  placeholder="${escapeHtml(placeholder)}"
43
44
  type="search"
@@ -521,6 +521,7 @@ export function renderRowEditorPanel({
521
521
  ' type="button" ',
522
522
  saving || deleting ? "disabled" : "",
523
523
  ">",
524
+ '<span class="material-symbols-outlined text-sm">delete</span>',
524
525
  escapeHtml(deleting ? "Deleting..." : deleteLabel),
525
526
  "</button>",
526
527
  ].join("")
@@ -19,6 +19,7 @@ const sidebarItems = [
19
19
  { label: 'TAGGING_QUEUE', href: '#/media-tagging/queue', key: 'mediaTaggingQueue' },
20
20
  ],
21
21
  },
22
+ { label: 'Backups', href: '#/backups', key: 'backups', icon: 'inventory_2' },
22
23
  { label: 'Settings', href: '#/settings', key: 'settings', icon: 'settings' },
23
24
  ];
24
25
 
@@ -11,6 +11,8 @@ export function parseHash(hash = window.location.hash) {
11
11
  switch (segments[0]) {
12
12
  case 'connections':
13
13
  return { name: 'connections', path: '/connections', params: {} };
14
+ case 'backups':
15
+ return { name: 'backups', path: '/backups', params: {} };
14
16
  case 'overview':
15
17
  return { name: 'overview', path: '/overview', params: {} };
16
18
  case 'charts':