sqlite-hub 0.4.0 → 0.6.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 (56) hide show
  1. package/README.md +2 -2
  2. package/changelog.md +15 -0
  3. package/frontend/assets/mockups/connections.png +0 -0
  4. package/frontend/assets/mockups/data.png +0 -0
  5. package/frontend/assets/mockups/data_row_editor.png +0 -0
  6. package/frontend/assets/mockups/home.png +0 -0
  7. package/frontend/assets/mockups/sql_editor.png +0 -0
  8. package/frontend/assets/mockups/sql_editor_querydetail.png +0 -0
  9. package/frontend/assets/mockups/structure.png +0 -0
  10. package/frontend/assets/mockups/structure_inspector.png +0 -0
  11. package/frontend/js/api.js +114 -5
  12. package/frontend/js/app.js +368 -18
  13. package/frontend/js/components/bottomTabs.js +1 -1
  14. package/frontend/js/components/dataGrid.js +3 -3
  15. package/frontend/js/components/queryEditor.js +33 -55
  16. package/frontend/js/components/queryHistoryDetail.js +263 -0
  17. package/frontend/js/components/queryHistoryPanel.js +228 -0
  18. package/frontend/js/components/queryResults.js +32 -46
  19. package/frontend/js/components/rowEditorPanel.js +73 -14
  20. package/frontend/js/components/sidebar.js +1 -0
  21. package/frontend/js/components/tableDesignerEditor.js +356 -0
  22. package/frontend/js/components/tableDesignerSidebar.js +126 -0
  23. package/frontend/js/components/tableDesignerSqlPreview.js +40 -0
  24. package/frontend/js/router.js +10 -0
  25. package/frontend/js/store.js +841 -22
  26. package/frontend/js/utils/format.js +23 -0
  27. package/frontend/js/utils/tableDesigner.js +1192 -0
  28. package/frontend/js/views/data.js +273 -250
  29. package/frontend/js/views/editor.js +34 -10
  30. package/frontend/js/views/overview.js +15 -0
  31. package/frontend/js/views/tableDesigner.js +37 -0
  32. package/frontend/styles/base.css +87 -73
  33. package/frontend/styles/components.css +841 -188
  34. package/frontend/styles/views.css +40 -0
  35. package/package.json +1 -1
  36. package/server/routes/data.js +2 -0
  37. package/server/routes/export.js +4 -1
  38. package/server/routes/overview.js +12 -0
  39. package/server/routes/sql.js +163 -5
  40. package/server/routes/tableDesigner.js +60 -0
  41. package/server/server.js +5 -1
  42. package/server/services/sqlite/dataBrowserService.js +4 -16
  43. package/server/services/sqlite/exportService.js +4 -16
  44. package/server/services/sqlite/overviewService.js +34 -0
  45. package/server/services/sqlite/sqlExecutor.js +83 -63
  46. package/server/services/sqlite/tableDesigner/changeAnalysis.js +295 -0
  47. package/server/services/sqlite/tableDesigner/schemaMapping.js +233 -0
  48. package/server/services/sqlite/tableDesigner/sql.js +63 -0
  49. package/server/services/sqlite/tableDesigner/validation.js +245 -0
  50. package/server/services/sqlite/tableDesignerService.js +181 -0
  51. package/server/services/sqlite/tableSort.js +63 -0
  52. package/server/services/storage/appStateStore.js +674 -1
  53. package/server/services/storage/queryHistoryUtils.js +169 -0
  54. package/frontend/assets/mockups/data_edit.png +0 -0
  55. package/frontend/assets/mockups/graph_visualize.png +0 -0
  56. package/frontend/assets/mockups/overview.png +0 -0
@@ -1,5 +1,7 @@
1
1
  import { renderBottomTabs } from "../components/bottomTabs.js";
2
2
  import { renderQueryEditor } from "../components/queryEditor.js";
3
+ import { renderQueryHistoryDetail } from "../components/queryHistoryDetail.js";
4
+ import { renderQueryHistoryPanel } from "../components/queryHistoryPanel.js";
3
5
  import { renderRowEditorPanel } from "../components/rowEditorPanel.js";
4
6
  import { renderQueryResultsPane } from "../components/queryResults.js";
5
7
  import { getCurrentConnection, getQueryMessages, getQueryPerformance } from "../store.js";
@@ -216,17 +218,17 @@ function renderResultsSurface(state, isResultsRoute) {
216
218
 
217
219
  content = state.connections.active
218
220
  ? renderQueryResultsPane(state.editor.result, {
219
- exporting: state.editor.exportLoading,
220
221
  selectedRowIndex: state.editor.selectedRowIndex,
221
222
  editable: editingState.enabled,
222
- editStatusMessage: editingState.message,
223
+ sortColumn: state.editor.resultSortColumn,
224
+ sortDirection: state.editor.resultSortDirection,
223
225
  })
224
226
  : renderMissingDatabase();
225
227
  }
226
228
 
227
229
  return `
228
230
  <div class="flex h-full min-h-0 flex-col border-t border-outline-variant/10 bg-surface-container-lowest">
229
- ${renderBottomTabs(activeTab, counts)}
231
+ ${renderBottomTabs(state.editor.activeTab, counts)}
230
232
  <div class="min-h-0 flex-1">${content}</div>
231
233
  </div>
232
234
  `;
@@ -234,13 +236,13 @@ function renderResultsSurface(state, isResultsRoute) {
234
236
 
235
237
  export function renderEditorView(state, { isResultsRoute = false } = {}) {
236
238
  const connection = getCurrentConnection(state);
237
- const editorSectionClass = isResultsRoute ? "h-1/4" : "min-h-[27.5%]";
238
- const resultsSectionClass = isResultsRoute ? "h-3/4" : "flex-1";
239
+ const editorSectionClass = "min-h-[27.5%]";
240
+ const resultsSectionClass = "flex-1";
239
241
 
240
242
  return {
241
243
  main: `
242
- <section class="view-surface flex h-full min-h-0 flex-col overflow-hidden">
243
- <div class="flex h-full min-h-0 flex-1 flex-col">
244
+ <section class="view-surface flex h-full min-h-0 flex-col overflow-hidden xl:flex-row">
245
+ <div class="flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden">
244
246
  <section class="${editorSectionClass} flex min-h-0 flex-col ${
245
247
  isResultsRoute ? "border-b-4 border-background" : ""
246
248
  }">
@@ -248,17 +250,39 @@ export function renderEditorView(state, { isResultsRoute = false } = {}) {
248
250
  query: state.editor.sqlText,
249
251
  executing: state.editor.executing,
250
252
  exporting: state.editor.exportLoading,
251
- history: state.editor.history,
252
253
  historyLoading: state.editor.historyLoading,
254
+ historyTotal: state.editor.historyTotal,
253
255
  title: connection?.label ?? "SQLite Query Workspace",
254
256
  })}
255
257
  </section>
256
- <section class="${resultsSectionClass} flex min-h-0 flex-col overflow-hidden">
258
+ <section class="${resultsSectionClass} flex min-h-0 min-w-0 flex-col overflow-hidden">
257
259
  ${renderResultsSurface(state, isResultsRoute)}
258
260
  </section>
259
261
  </div>
262
+ ${renderQueryHistoryPanel({
263
+ items: state.editor.history,
264
+ loading: state.editor.historyLoading,
265
+ loadingMore: state.editor.historyLoadingMore,
266
+ error: state.editor.historyError,
267
+ activeTab: state.editor.historyTab,
268
+ search: state.editor.historySearchInput,
269
+ total: state.editor.historyTotal,
270
+ hasMore: state.editor.historyHasMore,
271
+ activeHistoryId: state.editor.historyActiveId,
272
+ selectedHistoryId: state.editor.historySelectedId,
273
+ })}
260
274
  </section>
261
275
  `,
262
- panel: isResultsRoute ? renderEditorRowPanel(state) : "",
276
+ panel:
277
+ isResultsRoute && state.editor.selectedRowIndex !== null
278
+ ? renderEditorRowPanel(state)
279
+ : state.editor.historySelectedId
280
+ ? renderQueryHistoryDetail({
281
+ item: state.editor.historyDetail,
282
+ runs: state.editor.historyRuns,
283
+ loading: state.editor.historyDetailLoading,
284
+ error: state.editor.historyDetailError,
285
+ })
286
+ : "",
263
287
  };
264
288
  }
@@ -126,6 +126,21 @@ function renderOperationalSurface(overview) {
126
126
  )
127
127
  .join("")}
128
128
  </div>
129
+ ${
130
+ overview.file?.path
131
+ ? `
132
+ <div class="border-t border-outline-variant/10 px-4 py-3">
133
+ <button
134
+ class="toolbar-button border border-outline-variant/20 bg-surface-container px-3 py-1.5 text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface transition-colors hover:border-primary-container hover:text-primary-container"
135
+ data-action="open-overview-in-finder"
136
+ type="button"
137
+ >
138
+ Open In Finder
139
+ </button>
140
+ </div>
141
+ `
142
+ : ""
143
+ }
129
144
  </section>
130
145
  `;
131
146
  }
@@ -0,0 +1,37 @@
1
+ import { renderTableDesignerEditor } from "../components/tableDesignerEditor.js";
2
+ import { renderTableDesignerSidebar } from "../components/tableDesignerSidebar.js";
3
+ import { renderTableDesignerSqlPreview } from "../components/tableDesignerSqlPreview.js";
4
+ import { escapeHtml } from "../utils/format.js";
5
+
6
+ function renderRouteError(error) {
7
+ if (!error) {
8
+ return "";
9
+ }
10
+
11
+ return `
12
+ <div class="table-designer-route-error">
13
+ <div class="table-designer-route-error__code">${escapeHtml(error.code)}</div>
14
+ <div class="table-designer-route-error__text">${escapeHtml(error.message)}</div>
15
+ </div>
16
+ `;
17
+ }
18
+
19
+ export function renderTableDesignerView(state) {
20
+ return {
21
+ main: `
22
+ <section class="view-surface table-designer-view">
23
+ ${renderTableDesignerSidebar(state)}
24
+ <div class="table-designer-workspace">
25
+ ${renderRouteError(state.tableDesigner.error)}
26
+ <div class="table-designer-workspace__top">
27
+ ${renderTableDesignerEditor(state)}
28
+ </div>
29
+ <div class="table-designer-workspace__bottom">
30
+ ${renderTableDesignerSqlPreview(state.tableDesigner.draft)}
31
+ </div>
32
+ </div>
33
+ </section>
34
+ `,
35
+ panel: "",
36
+ };
37
+ }
@@ -1,103 +1,110 @@
1
1
  *,
2
2
  *::before,
3
3
  *::after {
4
- box-sizing: border-box;
4
+ box-sizing: border-box;
5
5
  }
6
6
 
7
7
  html,
8
8
  body {
9
- height: 100%;
10
- margin: 0;
9
+ height: 100%;
10
+ margin: 0;
11
11
  }
12
12
 
13
13
  body {
14
- background: var(--color-background);
15
- color: var(--color-on-surface);
16
- font-family: var(--font-family-body);
14
+ background: var(--color-background);
15
+ color: var(--color-on-surface);
16
+ font-family: var(--font-family-body);
17
17
  }
18
18
 
19
19
  button,
20
20
  input,
21
21
  select,
22
22
  textarea {
23
- color: inherit;
24
- font: inherit;
23
+ color: inherit;
24
+ font: inherit;
25
25
  }
26
26
 
27
27
  button {
28
- cursor: pointer;
28
+ cursor: pointer;
29
29
  }
30
30
 
31
31
  a {
32
- color: inherit;
33
- text-decoration: none;
32
+ color: inherit;
33
+ text-decoration: none;
34
34
  }
35
35
 
36
36
  img {
37
- display: block;
38
- max-width: 100%;
37
+ display: block;
38
+ max-width: 100%;
39
39
  }
40
40
 
41
41
  [hidden] {
42
- display: none !important;
42
+ display: none !important;
43
43
  }
44
44
 
45
45
  .font-headline {
46
- font-family: var(--font-family-headline);
46
+ font-family: var(--font-family-headline);
47
47
  }
48
48
 
49
49
  .font-body {
50
- font-family: var(--font-family-body);
50
+ font-family: var(--font-family-body);
51
51
  }
52
52
 
53
53
  .font-mono {
54
- font-family: var(--font-family-mono);
54
+ font-family: var(--font-family-mono);
55
+ overflow: hidden;
56
+ white-space: nowrap;
57
+ text-overflow: ellipsis;
55
58
  }
56
59
 
57
60
  .material-symbols-outlined {
58
- display: inline-block;
59
- font-size: var(--font-size-icon);
60
- font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 24;
61
- letter-spacing: normal;
62
- line-height: 1;
63
- text-transform: none;
64
- white-space: nowrap;
65
- word-wrap: normal;
66
- direction: ltr;
67
- vertical-align: middle;
61
+ display: inline-block;
62
+ font-size: var(--font-size-icon);
63
+ font-variation-settings:
64
+ 'FILL' 0,
65
+ 'wght' 400,
66
+ 'GRAD' 0,
67
+ 'opsz' 24;
68
+ letter-spacing: normal;
69
+ line-height: 1;
70
+ text-transform: none;
71
+ white-space: nowrap;
72
+ word-wrap: normal;
73
+ direction: ltr;
74
+ vertical-align: middle;
68
75
  }
69
76
 
70
77
  .no-scrollbar {
71
- -ms-overflow-style: none;
72
- scrollbar-width: none;
78
+ -ms-overflow-style: none;
79
+ scrollbar-width: none;
73
80
  }
74
81
 
75
82
  .no-scrollbar::-webkit-scrollbar {
76
- display: none;
77
- height: 0;
78
- width: 0;
83
+ display: none;
84
+ height: 0;
85
+ width: 0;
79
86
  }
80
87
 
81
88
  .custom-scrollbar {
82
- scrollbar-color: var(--color-surface-highest) var(--color-surface-lowest);
83
- scrollbar-width: thin;
89
+ scrollbar-color: var(--color-surface-highest) var(--color-surface-lowest);
90
+ scrollbar-width: thin;
84
91
  }
85
92
 
86
93
  .custom-scrollbar::-webkit-scrollbar {
87
- height: var(--scrollbar-size);
88
- width: var(--scrollbar-size);
94
+ height: var(--scrollbar-size);
95
+ width: var(--scrollbar-size);
89
96
  }
90
97
 
91
98
  .custom-scrollbar::-webkit-scrollbar-track {
92
- background: var(--color-surface-lowest);
99
+ background: var(--color-surface-lowest);
93
100
  }
94
101
 
95
102
  .custom-scrollbar::-webkit-scrollbar-thumb {
96
- background: var(--color-surface-highest);
103
+ background: var(--color-surface-highest);
97
104
  }
98
105
 
99
106
  .custom-scrollbar::-webkit-scrollbar-thumb:hover {
100
- background: var(--color-primary-container);
107
+ background: var(--color-primary-container);
101
108
  }
102
109
 
103
110
  .clipped-btn,
@@ -105,72 +112,79 @@ img {
105
112
  .custom-clip,
106
113
  .clip-button,
107
114
  .clip-corner {
108
- clip-path: var(--clip-path);
115
+ clip-path: var(--clip-path);
109
116
  }
110
117
 
111
118
  .sql-keyword {
112
- color: var(--color-primary-container);
119
+ color: var(--color-primary-container);
113
120
  }
114
121
 
115
122
  .sql-string {
116
- color: var(--color-on-surface);
123
+ color: var(--color-on-surface);
117
124
  }
118
125
 
119
126
  .sql-comment {
120
- color: var(--color-outline-variant);
127
+ color: var(--color-outline-variant);
121
128
  }
122
129
 
123
130
  .sql-value {
124
- color: var(--color-on-surface-variant);
131
+ color: var(--color-on-surface-variant);
125
132
  }
126
133
 
127
134
  .query-editor-layer {
128
- min-height: 140px;
135
+ display: grid;
136
+ min-height: 140px;
137
+ overflow: hidden;
129
138
  }
130
139
 
131
140
  .query-editor-highlight,
132
141
  .query-editor-input {
133
- font: inherit;
134
- line-height: inherit;
135
- margin: 0;
136
- padding: 0;
137
- tab-size: 2;
138
- white-space: pre-wrap;
139
- word-break: break-word;
142
+ display: block;
143
+ font: inherit;
144
+ grid-area: 1 / 1;
145
+ line-height: inherit;
146
+ margin: 0;
147
+ min-height: 100%;
148
+ padding: 0;
149
+ tab-size: 2;
150
+ white-space: pre-wrap;
151
+ width: 100%;
152
+ word-break: normal;
153
+ overflow-wrap: anywhere;
140
154
  }
141
155
 
142
156
  .query-editor-highlight {
143
- color: var(--color-on-surface);
144
- inset: 0;
145
- overflow: hidden;
146
- pointer-events: none;
147
- position: absolute;
157
+ color: var(--color-on-surface);
158
+ overflow: hidden;
159
+ pointer-events: none;
148
160
  }
149
161
 
150
162
  .query-editor-input {
151
- -webkit-text-fill-color: transparent;
152
- background: transparent;
153
- caret-color: var(--color-on-surface);
154
- color: transparent;
155
- overflow: auto;
163
+ -webkit-appearance: none;
164
+ -webkit-text-fill-color: transparent;
165
+ appearance: none;
166
+ background: transparent;
167
+ caret-color: var(--color-on-surface);
168
+ color: transparent;
169
+ overflow: auto;
156
170
  }
157
171
 
158
172
  .query-editor-input::placeholder {
159
- color: transparent;
173
+ color: transparent;
160
174
  }
161
175
 
162
176
  .cursor-blink {
163
- animation: cursor-blink var(--duration-cursor-blink) step-end infinite;
164
- border-right: 2px solid var(--color-primary-container);
177
+ animation: cursor-blink var(--duration-cursor-blink) step-end infinite;
178
+ border-right: 2px solid var(--color-primary-container);
165
179
  }
166
180
 
167
181
  @keyframes cursor-blink {
168
- from,
169
- to {
170
- border-color: transparent;
171
- }
172
-
173
- 50% {
174
- border-color: var(--color-primary-container);
175
- }
182
+ from,
183
+ to {
184
+ border-color: transparent;
185
+ }
186
+
187
+ 50% {
188
+ border-color: var(--color-primary-container);
189
+ }
176
190
  }