sqlite-hub 2.0.1 → 2.2.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 (98) hide show
  1. package/README.md +21 -9
  2. package/docs/MCP.md +8 -0
  3. package/docs/changelog.md +16 -0
  4. package/docs/guidelines/DESIGN.md +64 -0
  5. package/docs/todo.md +2 -0
  6. package/frontend/assets/mockups/backups_1_1920.webp +0 -0
  7. package/frontend/assets/mockups/backups_2_compare_drawer_1920.webp +0 -0
  8. package/frontend/assets/mockups/backups_3_create_backup_modal_1920.webp +0 -0
  9. package/frontend/assets/mockups/backups_4_edit_backup_modal_1920.webp +0 -0
  10. package/frontend/assets/mockups/backups_5_restore_backup_modal_1920.webp +0 -0
  11. package/frontend/assets/mockups/backups_6_delete_backup_modal_1920.webp +0 -0
  12. package/frontend/assets/mockups/charts_1_1920.webp +0 -0
  13. package/frontend/assets/mockups/charts_2_query_detail_1920.webp +0 -0
  14. package/frontend/assets/mockups/charts_3_create_query_chart_modal_1920.webp +0 -0
  15. package/frontend/assets/mockups/charts_4_edit_query_chart_modal_1920.webp +0 -0
  16. package/frontend/assets/mockups/charts_5_delete_query_chart_modal_1920.webp +0 -0
  17. package/frontend/assets/mockups/charts_6_copy_column_modal_1920.webp +0 -0
  18. package/frontend/assets/mockups/connections_1_1920.webp +0 -0
  19. package/frontend/assets/mockups/connections_2_create_connection_modal_1920.webp +0 -0
  20. package/frontend/assets/mockups/connections_3_open_connection_modal_1920.webp +0 -0
  21. package/frontend/assets/mockups/data_1_1920.webp +0 -0
  22. package/frontend/assets/mockups/data_2_roweditor_1920.webp +0 -0
  23. package/frontend/assets/mockups/data_3_generate_data_modal_1920.webp +0 -0
  24. package/frontend/assets/mockups/data_4_data_export_modal_1920.webp +0 -0
  25. package/frontend/assets/mockups/documents_1_1920.webp +0 -0
  26. package/frontend/assets/mockups/documents_2_document_insert_table_modal_1920.webp +0 -0
  27. package/frontend/assets/mockups/documents_3_document_insert_note_modal_1920.webp +0 -0
  28. package/frontend/assets/mockups/logs_1_1920.webp +0 -0
  29. package/frontend/assets/mockups/media_tagging_queue_1_1920.webp +0 -0
  30. package/frontend/assets/mockups/media_tagging_setup_1_1920.webp +0 -0
  31. package/frontend/assets/mockups/media_tagging_setup_2_create_media_tagging_tag_table_modal_1920.webp +0 -0
  32. package/frontend/assets/mockups/media_tagging_setup_3_create_media_tagging_mapping_table_modal_1920.webp +0 -0
  33. package/frontend/assets/mockups/overview_1_1920.webp +0 -0
  34. package/frontend/assets/mockups/settings_1_1920.webp +0 -0
  35. package/frontend/assets/mockups/sql_editor_1_1920.webp +0 -0
  36. package/frontend/assets/mockups/sql_editor_2_query_detail_1920.webp +0 -0
  37. package/frontend/assets/mockups/sql_editor_3_query_export_modal_1920.webp +0 -0
  38. package/frontend/assets/mockups/structure_1_1920.webp +0 -0
  39. package/frontend/assets/mockups/structure_2_generate_types_modal_1920.webp +0 -0
  40. package/frontend/assets/mockups/structure_3_generate_types_modal_1920.webp +0 -0
  41. package/frontend/assets/mockups/table_designer_1_1920.webp +0 -0
  42. package/frontend/assets/mockups/table_designer_2_table_designer_constraints_modal_1920.webp +0 -0
  43. package/frontend/js/api.js +18 -0
  44. package/frontend/js/app.js +567 -15
  45. package/frontend/js/components/badges.js +16 -0
  46. package/frontend/js/components/connectionCard.js +35 -0
  47. package/frontend/js/components/modal.js +268 -247
  48. package/frontend/js/components/queryHistoryDetail.js +25 -45
  49. package/frontend/js/components/rowEditorPanel.js +7 -60
  50. package/frontend/js/components/structureGraph.js +292 -13
  51. package/frontend/js/components/tableDesignerConstraintsDrawer.js +400 -0
  52. package/frontend/js/components/tableDesignerEditor.js +51 -16
  53. package/frontend/js/components/tableDesignerSidebar.js +9 -5
  54. package/frontend/js/store.js +1847 -190
  55. package/frontend/js/utils/connectionRegistry.js +76 -0
  56. package/frontend/js/utils/markdownDocuments.js +21 -4
  57. package/frontend/js/utils/tableDesigner.js +431 -62
  58. package/frontend/js/views/backups.js +2 -5
  59. package/frontend/js/views/charts.js +20 -10
  60. package/frontend/js/views/connections.js +101 -6
  61. package/frontend/js/views/data.js +52 -9
  62. package/frontend/js/views/documents.js +153 -59
  63. package/frontend/js/views/structure.js +28 -5
  64. package/frontend/js/views/tableAdvisor.js +14 -6
  65. package/frontend/js/views/tableDesigner.js +44 -9
  66. package/frontend/styles/base.css +9 -0
  67. package/frontend/styles/components.css +676 -79
  68. package/frontend/styles/structure-graph.css +49 -0
  69. package/frontend/styles/tailwind.generated.css +1 -1
  70. package/frontend/styles/views.css +12 -0
  71. package/package.json +1 -1
  72. package/server/routes/backups.js +37 -1
  73. package/server/routes/charts.js +28 -0
  74. package/server/routes/connections.js +12 -0
  75. package/server/routes/data.js +30 -2
  76. package/server/routes/documents.js +61 -5
  77. package/server/routes/settings.js +32 -0
  78. package/server/routes/tableDesigner.js +35 -2
  79. package/server/server.js +3 -3
  80. package/server/services/mcpToolService.js +30 -0
  81. package/server/services/sqlite/connectionManager.js +25 -2
  82. package/server/services/sqlite/dataBrowserService.js +21 -0
  83. package/server/services/sqlite/introspection.js +39 -1
  84. package/server/services/sqlite/overviewService.js +1 -0
  85. package/server/services/sqlite/structureService.js +66 -11
  86. package/server/services/sqlite/tableAdvisor.js +6 -0
  87. package/server/services/sqlite/tableDesigner/schemaMapping.js +7 -1
  88. package/server/services/sqlite/tableDesigner/sql.js +31 -2
  89. package/server/services/sqlite/tableDesigner/validation.js +69 -9
  90. package/server/services/sqlite/tableDesignerService.js +110 -5
  91. package/server/services/storage/appStateStore.js +590 -10
  92. package/server/utils/fileValidation.js +5 -0
  93. package/server/utils/userActionLog.js +49 -0
  94. package/frontend/assets/mockups/backups_2_create_backup_modal_1920.webp +0 -0
  95. package/frontend/assets/mockups/backups_3_edit_backup_modal_1920.webp +0 -0
  96. package/frontend/assets/mockups/backups_4_restore_backup_modal_1920.webp +0 -0
  97. package/frontend/assets/mockups/backups_5_delete_backup_modal_1920.webp +0 -0
  98. package/frontend/assets/mockups/data_3_data_export_modal_1920.webp +0 -0
@@ -1,4 +1,5 @@
1
1
  import { clearInspector, renderDdlSection, renderInspector } from '../components/structureGraph.js';
2
+ import { renderVirtualTableBadge } from '../components/badges.js';
2
3
  import { renderDropdownButton } from '../components/dropdownButton.js';
3
4
  import { renderGenerateTypesDropdown } from '../components/generateTypesDropdown.js';
4
5
  import { renderWorkspaceOpenDropdown } from '../components/workspaceOpenDropdown.js';
@@ -36,11 +37,14 @@ function renderEntryGroup(title, entries, activeName, options = {}) {
36
37
  '" ',
37
38
  entryAttributes,
38
39
  ' type="button">',
39
- '<div class="font-mono text-xs ',
40
+ '<div class="flex min-w-0 items-center gap-2">',
41
+ '<div class="min-w-0 flex-1 truncate font-mono text-xs ',
40
42
  isActive ? 'text-primary-container' : 'text-on-surface',
41
43
  '">',
42
44
  escapeHtml(entry.name),
43
45
  '</div>',
46
+ renderVirtualTableBadge(entry),
47
+ '</div>',
44
48
  metaMarkup,
45
49
  '</button>',
46
50
  ].join('');
@@ -76,6 +80,10 @@ function getFilteredTables(tables, searchQuery) {
76
80
  return tables.filter(table => table.name.toLowerCase().includes(normalizedSearch));
77
81
  }
78
82
 
83
+ function isShadowTable(table) {
84
+ return Boolean(table?.isShadow);
85
+ }
86
+
79
87
  function renderStructureTableSearch(state) {
80
88
  return `
81
89
  <label class="table-designer-sidebar__search">
@@ -129,7 +137,10 @@ function renderObjectInspector(detail) {
129
137
  <span class="material-symbols-outlined text-[18px]">close</span>
130
138
  </button>
131
139
  </div>
132
- <div class="structure-graph__title">${escapeHtml(detail.name)}</div>
140
+ <div class="flex min-w-0 flex-wrap items-center gap-2">
141
+ <div class="structure-graph__title min-w-0">${escapeHtml(detail.name)}</div>
142
+ ${renderVirtualTableBadge(detail)}
143
+ </div>
133
144
  ${
134
145
  detail.tableName
135
146
  ? `
@@ -336,9 +347,10 @@ function renderGraphSurface(structure, selectedName, detail, detailLoading, tabl
336
347
 
337
348
  export function renderStructureView(state) {
338
349
  const structure = state.structure.data;
339
- const filteredTables = structure
340
- ? getFilteredTables(structure.grouped.tables ?? [], state.structure.tableSearchQuery)
341
- : [];
350
+ const regularTables = (structure?.grouped.tables ?? []).filter(table => !isShadowTable(table));
351
+ const shadowTables = (structure?.grouped.tables ?? []).filter(isShadowTable);
352
+ const filteredTables = structure ? getFilteredTables(regularTables, state.structure.tableSearchQuery) : [];
353
+ const filteredShadowTables = structure ? getFilteredTables(shadowTables, state.structure.tableSearchQuery) : [];
342
354
  const detail = state.structure.detail?.name === state.structure.selectedName ? state.structure.detail : null;
343
355
  const tablesVisible = state.structure.tablesVisible !== false;
344
356
 
@@ -376,6 +388,17 @@ export function renderStructureView(state) {
376
388
  ? 'tables match the current search.'
377
389
  : null,
378
390
  })}
391
+ ${
392
+ shadowTables.length
393
+ ? renderEntryGroup('Shadow Tables', filteredShadowTables, state.structure.selectedName, {
394
+ compact: true,
395
+ showMeta: false,
396
+ emptyMessage: state.structure.tableSearchQuery
397
+ ? 'shadow tables match the current search.'
398
+ : null,
399
+ })
400
+ : ''
401
+ }
379
402
  ${renderEntryGroup('Views', structure.grouped.views, state.structure.selectedName)}
380
403
  ${renderEntryGroup('Indexes', structure.grouped.indexes, state.structure.selectedName, {
381
404
  compact: true,
@@ -5,6 +5,7 @@ import {
5
5
  highlightSql,
6
6
  truncateMiddle,
7
7
  } from '../utils/format.js';
8
+ import { renderVirtualTableBadge } from '../components/badges.js';
8
9
  import { renderWorkspaceOpenDropdown } from '../components/workspaceOpenDropdown.js';
9
10
 
10
11
  const CATEGORY_LABELS = {
@@ -73,7 +74,8 @@ function renderRiskBadge(risk = 'low') {
73
74
  }
74
75
 
75
76
  function renderTableList(state) {
76
- const { tables = [], selectedTableName } = state.tableAdvisor;
77
+ const { selectedTableName } = state.tableAdvisor;
78
+ const tables = (state.tableAdvisor.tables ?? []).filter(table => !table?.isShadow);
77
79
 
78
80
  if (state.tableAdvisor.loading && !tables.length) {
79
81
  return `
@@ -107,8 +109,11 @@ function renderTableList(state) {
107
109
  href="#/table-advisor/${encodeURIComponent(table.name)}"
108
110
  title="${escapeHtml(table.name)}"
109
111
  >
110
- <div class="table-designer-sidebar__item-name ${isActive ? 'is-active' : ''}">
111
- ${escapeHtml(table.name)}
112
+ <div class="flex min-w-0 items-center gap-2">
113
+ <div class="table-designer-sidebar__item-name min-w-0 flex-1 ${isActive ? 'is-active' : ''}">
114
+ ${escapeHtml(table.name)}
115
+ </div>
116
+ ${renderVirtualTableBadge(table)}
112
117
  </div>
113
118
  <div class="table-designer-sidebar__item-meta">
114
119
  ${escapeHtml(formatNumber(table.columnCount ?? 0))} columns
@@ -128,9 +133,12 @@ function renderSummary(result = {}) {
128
133
  <div class="grid grid-cols-1 gap-3 md:grid-cols-4">
129
134
  <div class="border border-outline-variant/10 bg-surface-container-low px-4 py-3">
130
135
  <div class="font-mono text-[10px] uppercase tracking-[0.2em] text-on-surface-variant/55">Table</div>
131
- <div class="mt-2 min-w-0 truncate text-lg font-black text-on-surface" title="${escapeHtml(
132
- result.tableName ?? '',
133
- )}">${escapeHtml(result.tableName ?? 'Unknown')}</div>
136
+ <div class="mt-2 flex min-w-0 items-center gap-2">
137
+ <div class="min-w-0 truncate text-lg font-black text-on-surface" title="${escapeHtml(
138
+ result.tableName ?? '',
139
+ )}">${escapeHtml(result.tableName ?? 'Unknown')}</div>
140
+ ${renderVirtualTableBadge(result)}
141
+ </div>
134
142
  </div>
135
143
  <div class="border border-outline-variant/10 bg-surface-container-low px-4 py-3">
136
144
  <div class="font-mono text-[10px] uppercase tracking-[0.2em] text-on-surface-variant/55">Score</div>
@@ -1,6 +1,8 @@
1
1
  import { renderTableDesignerEditor } from "../components/tableDesignerEditor.js";
2
+ import { renderTableDesignerConstraintsDrawer } from "../components/tableDesignerConstraintsDrawer.js";
2
3
  import { renderTableDesignerSidebar } from "../components/tableDesignerSidebar.js";
3
4
  import { renderTableDesignerSqlPreview } from "../components/tableDesignerSqlPreview.js";
5
+ import { renderDropdownButton } from "../components/dropdownButton.js";
4
6
  import { renderWorkspaceOpenDropdown } from "../components/workspaceOpenDropdown.js";
5
7
  import { escapeHtml } from "../utils/format.js";
6
8
 
@@ -17,6 +19,44 @@ function renderRouteError(error) {
17
19
  `;
18
20
  }
19
21
 
22
+ function renderImportDropdown() {
23
+ return renderDropdownButton({
24
+ align: "right",
25
+ icon: "upload_file",
26
+ label: "Import",
27
+ title: "Import data",
28
+ items: [
29
+ {
30
+ action: "import-table-designer-data",
31
+ dataAttributes: {
32
+ importFormat: "csv",
33
+ importAccept: ".csv,text/csv",
34
+ },
35
+ icon: "table_chart",
36
+ label: "CSV",
37
+ },
38
+ {
39
+ action: "import-table-designer-data",
40
+ dataAttributes: {
41
+ importFormat: "tsv",
42
+ importAccept: ".tsv,text/tab-separated-values",
43
+ },
44
+ icon: "table_rows",
45
+ label: "TSV",
46
+ },
47
+ {
48
+ action: "import-table-designer-data",
49
+ dataAttributes: {
50
+ importFormat: "json",
51
+ importAccept: ".json,application/json",
52
+ },
53
+ icon: "data_object",
54
+ label: "JSON",
55
+ },
56
+ ],
57
+ });
58
+ }
59
+
20
60
  function renderWorkspaceToolbar(state) {
21
61
  const tablesVisible = state.tableDesigner.tablesVisible !== false;
22
62
  const tableName = state.tableDesigner.selectedTableName ?? state.tableDesigner.draft?.tableName ?? "";
@@ -62,17 +102,12 @@ function renderWorkspaceToolbar(state) {
62
102
  },
63
103
  ],
64
104
  })}
65
- <button
66
- class="standard-button"
67
- data-action="import-table-designer-csv"
68
- type="button"
69
- >
70
- Import CSV
71
- </button>
105
+ ${renderImportDropdown()}
72
106
  <input
73
- accept=".csv,text/csv"
107
+ accept=".csv,text/csv,.tsv,text/tab-separated-values,.json,application/json"
74
108
  class="table-designer-workspace__file-input"
75
109
  data-bind="table-designer-import-file"
110
+ data-import-format="csv"
76
111
  type="file"
77
112
  />
78
113
  <button
@@ -110,6 +145,6 @@ export function renderTableDesignerView(state) {
110
145
  </div>
111
146
  </section>
112
147
  `,
113
- panel: "",
148
+ panel: renderTableDesignerConstraintsDrawer(state),
114
149
  };
115
150
  }
@@ -70,6 +70,15 @@ textarea:disabled {
70
70
  padding: 0 var(--control-padding-inline);
71
71
  }
72
72
 
73
+ .control-input:focus,
74
+ .control-input:focus-visible,
75
+ .control-select:focus,
76
+ .control-select:focus-visible {
77
+ border-color: var(--color-primary-container);
78
+ box-shadow: none;
79
+ outline: none;
80
+ }
81
+
73
82
  input.control-input--ghost {
74
83
  appearance: none;
75
84
  background: transparent;