sqlite-hub 0.6.0 → 0.8.7

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 (44) hide show
  1. package/README.md +107 -0
  2. package/bin/sqlite-hub.js +285 -4
  3. package/changelog.md +21 -0
  4. package/docs/DESIGN_GUIDELINES.md +36 -0
  5. package/frontend/assets/mockups/sql_editor_croped.png +0 -0
  6. package/frontend/index.html +80 -0
  7. package/frontend/js/api.js +34 -0
  8. package/frontend/js/app.js +188 -10
  9. package/frontend/js/components/connectionCard.js +3 -4
  10. package/frontend/js/components/emptyState.js +4 -4
  11. package/frontend/js/components/modal.js +341 -24
  12. package/frontend/js/components/queryChartRenderer.js +125 -0
  13. package/frontend/js/components/queryEditor.js +33 -6
  14. package/frontend/js/components/queryHistoryDetail.js +16 -7
  15. package/frontend/js/components/queryHistoryPanel.js +18 -7
  16. package/frontend/js/components/rowEditorPanel.js +59 -54
  17. package/frontend/js/components/sidebar.js +32 -32
  18. package/frontend/js/components/structureGraph.js +54 -122
  19. package/frontend/js/components/tableDesignerEditor.js +9 -8
  20. package/frontend/js/components/tableDesignerSidebar.js +52 -48
  21. package/frontend/js/components/tableDesignerSqlPreview.js +60 -28
  22. package/frontend/js/lib/queryChartOptions.js +283 -0
  23. package/frontend/js/lib/queryCharts.js +560 -0
  24. package/frontend/js/router.js +8 -0
  25. package/frontend/js/store.js +641 -0
  26. package/frontend/js/views/charts.js +499 -0
  27. package/frontend/js/views/connections.js +5 -17
  28. package/frontend/js/views/data.js +40 -27
  29. package/frontend/js/views/editor.js +19 -13
  30. package/frontend/js/views/overview.js +149 -10
  31. package/frontend/js/views/settings.js +2 -2
  32. package/frontend/js/views/structure.js +105 -59
  33. package/frontend/js/views/tableDesigner.js +7 -2
  34. package/frontend/styles/base.css +62 -0
  35. package/frontend/styles/components.css +68 -118
  36. package/frontend/styles/structure-graph.css +19 -82
  37. package/frontend/styles/tokens.css +2 -0
  38. package/frontend/styles/views.css +293 -66
  39. package/package.json +2 -1
  40. package/server/routes/charts.js +153 -0
  41. package/server/server.js +6 -0
  42. package/server/services/sqlite/overviewService.js +68 -0
  43. package/server/services/storage/appStateStore.js +400 -1
  44. package/server/services/storage/queryHistoryChartUtils.js +145 -0
@@ -1,12 +1,11 @@
1
- import { renderPageHeader } from "../components/pageHeader.js";
2
- import { clearInspector, renderInspector } from "../components/structureGraph.js";
1
+ import { clearInspector, renderDdlSection, renderInspector } from "../components/structureGraph.js";
3
2
  import { escapeHtml, formatNumber } from "../utils/format.js";
4
3
 
5
4
  function renderEntryGroup(title, entries, activeName, options = {}) {
6
5
  const { compact = false, showMeta = true } = options;
7
6
 
8
7
  return `
9
- <section class="shell-section flex flex-col p-5">
8
+ <section class="structure-sidebar__section">
10
9
  <div class="mb-4 shrink-0 text-[10px] font-bold uppercase tracking-[0.25em] text-primary-container">
11
10
  ${escapeHtml(title)}
12
11
  </div>
@@ -173,20 +172,16 @@ function renderObjectInspector(detail) {
173
172
  : ""
174
173
  }
175
174
 
176
- <section class="structure-graph__section">
177
- <div class="structure-graph__section-title">DDL</div>
178
- <pre class="structure-graph__ddl custom-scrollbar">${escapeHtml(
179
- detail.ddl || "No DDL available."
180
- )}</pre>
181
- </section>
175
+ ${renderDdlSection(detail.ddl)}
182
176
  </div>
183
177
  `;
184
178
  }
185
179
 
186
- function renderGraphSurface(structure, selectedName, detail, detailLoading) {
180
+ function renderGraphSurface(structure, selectedName, detail, detailLoading, tablesVisible = true) {
187
181
  const graph = structure?.graph ?? { tables: [], relationshipCount: 0 };
188
182
  const selectedGraphTable =
189
183
  graph.tables?.find((table) => table.name === selectedName && table.type === "table") ?? null;
184
+ const toolbarButtonClass = "standard-button";
190
185
  const inspectorMarkup = selectedGraphTable
191
186
  ? renderInspector(selectedGraphTable)
192
187
  : detailLoading
@@ -199,20 +194,20 @@ function renderGraphSurface(structure, selectedName, detail, detailLoading) {
199
194
  <section class="structure-graph" data-structure-graph-root>
200
195
  <div class="structure-graph__toolbar">
201
196
  <div class="structure-graph__toolbar-main">
202
- <label class="structure-graph__search" data-structure-graph-search>
203
- <span class="material-symbols-outlined structure-graph__search-icon">search</span>
204
- <input
205
- class="structure-graph__search-input"
206
- data-structure-graph-search-input
207
- placeholder="Find table"
208
- spellcheck="false"
209
- type="search"
210
- />
211
- </label>
197
+ <button
198
+ class="${toolbarButtonClass}"
199
+ data-action="toggle-structure-tables"
200
+ type="button"
201
+ >
202
+ <span class="material-symbols-outlined text-sm">${
203
+ tablesVisible ? "visibility_off" : "visibility"
204
+ }</span>
205
+ ${tablesVisible ? "Hide Tables" : "Show Tables"}
206
+ </button>
212
207
  </div>
213
208
  <div class="structure-graph__toolbar-actions">
214
209
  <button
215
- class="structure-graph__button"
210
+ class="${toolbarButtonClass}"
216
211
  data-structure-graph-action="fit"
217
212
  type="button"
218
213
  >
@@ -220,7 +215,7 @@ function renderGraphSurface(structure, selectedName, detail, detailLoading) {
220
215
  Fit Graph
221
216
  </button>
222
217
  <button
223
- class="structure-graph__button"
218
+ class="${toolbarButtonClass}"
224
219
  data-structure-graph-action="relayout"
225
220
  type="button"
226
221
  >
@@ -228,7 +223,7 @@ function renderGraphSurface(structure, selectedName, detail, detailLoading) {
228
223
  Recalculate Layout
229
224
  </button>
230
225
  <button
231
- class="structure-graph__button"
226
+ class="${toolbarButtonClass}"
232
227
  data-structure-graph-action="clear"
233
228
  type="button"
234
229
  >
@@ -236,7 +231,7 @@ function renderGraphSurface(structure, selectedName, detail, detailLoading) {
236
231
  Clear Selection
237
232
  </button>
238
233
  <button
239
- class="structure-graph__button is-disabled"
234
+ class="${toolbarButtonClass}"
240
235
  data-structure-graph-action="open-data"
241
236
  disabled
242
237
  type="button"
@@ -245,7 +240,7 @@ function renderGraphSurface(structure, selectedName, detail, detailLoading) {
245
240
  Open Data
246
241
  </button>
247
242
  <button
248
- class="structure-graph__button"
243
+ class="${toolbarButtonClass}"
249
244
  data-structure-graph-action="toggle-inspector"
250
245
  type="button"
251
246
  >
@@ -282,44 +277,63 @@ function renderGraphSurface(structure, selectedName, detail, detailLoading) {
282
277
  `;
283
278
  }
284
279
 
280
+ function renderStructureWorkspaceHeader(structure, selectedName) {
281
+ const tableCount = structure?.grouped?.tables?.length ?? 0;
282
+ const viewCount = structure?.grouped?.views?.length ?? 0;
283
+
284
+ return `
285
+ <header class="structure-view__header">
286
+ <div class="structure-headline-container">
287
+ <div class="text-[10px] font-bold uppercase tracking-[0.2em] text-primary-container">
288
+ Schema Graph
289
+ </div>
290
+ <h1 class="mt-2 font-headline text-4xl font-black uppercase tracking-tight text-primary-container">
291
+ Structure
292
+ </h1>
293
+ <div class="mt-2 text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
294
+ ${
295
+ selectedName
296
+ ? `selected ${escapeHtml(selectedName)} // tables ${escapeHtml(formatNumber(tableCount))} // views ${escapeHtml(formatNumber(viewCount))}`
297
+ : `tables ${escapeHtml(formatNumber(tableCount))} // views ${escapeHtml(formatNumber(viewCount))}`
298
+ }
299
+ </div>
300
+ </div>
301
+ </header>
302
+ `;
303
+ }
304
+
285
305
  export function renderStructureView(state) {
286
306
  const structure = state.structure.data;
287
307
  const detail =
288
308
  state.structure.detail?.name === state.structure.selectedName ? state.structure.detail : null;
309
+ const tablesVisible = state.structure.tablesVisible !== false;
289
310
 
290
311
  return {
291
312
  main: `
292
- <section class="view-surface flex h-full min-h-0 flex-col bg-surface-container">
293
- <div class="view-frame flex h-full min-h-0 flex-col">
294
- ${renderPageHeader({
295
- title: "Structure",
296
- subtitle: "Schema graph, foreign-key paths, raw DDL, and object metadata",
297
- })}
298
-
299
- ${
300
- state.structure.loading && !structure
301
- ? `
302
- <div class="flex min-h-0 flex-1 items-center justify-center border border-outline-variant/10 bg-surface-container-low">
303
- <div class="text-center text-on-surface-variant/40">
304
- <span class="material-symbols-outlined mb-3 text-4xl">progress_activity</span>
305
- <p class="font-mono text-[10px] uppercase tracking-[0.22em]">LOADING_STRUCTURE</p>
313
+ <section class="view-surface structure-view">
314
+ ${
315
+ tablesVisible
316
+ ? `
317
+ <aside class="structure-view__sidebar">
318
+ <div class="structure-view__sidebar-header">
319
+ <div class="text-[10px] font-bold uppercase tracking-[0.2em] text-primary-container">
320
+ Objects
321
+ </div>
322
+ <div class="mt-2 text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
323
+ total ${escapeHtml(
324
+ formatNumber(
325
+ (structure?.grouped?.tables?.length ?? 0) +
326
+ (structure?.grouped?.views?.length ?? 0) +
327
+ (structure?.grouped?.indexes?.length ?? 0) +
328
+ (structure?.grouped?.triggers?.length ?? 0)
329
+ )
330
+ )}
306
331
  </div>
307
332
  </div>
308
- `
309
- : state.structure.error
310
- ? `
311
- <div class="min-h-0 flex-1 border border-error/20 bg-error-container/10 px-6 py-5 text-sm text-on-surface">
312
- <div class="font-headline text-xs font-bold uppercase tracking-[0.18em] text-error">
313
- ${escapeHtml(state.structure.error.code)}
314
- </div>
315
- <div class="mt-2">${escapeHtml(state.structure.error.message)}</div>
316
- </div>
317
- `
318
- : structure
319
- ? `
320
- <section class="grid min-h-0 flex-1 grid-cols-1 gap-6 xl:grid-cols-[18.5rem_minmax(0,1fr)] 2xl:grid-cols-[19.5rem_minmax(0,1fr)]">
321
- <div class="custom-scrollbar min-h-0 overflow-y-auto pr-1">
322
- <div class="space-y-6">
333
+ <div class="structure-view__sidebar-body custom-scrollbar">
334
+ ${
335
+ structure
336
+ ? `
323
337
  ${renderEntryGroup(
324
338
  "Tables",
325
339
  structure.grouped.tables,
@@ -342,19 +356,51 @@ export function renderStructureView(state) {
342
356
  structure.grouped.triggers,
343
357
  state.structure.selectedName
344
358
  )}
345
- </div>
346
- </div>
359
+ `
360
+ : ""
361
+ }
362
+ </div>
363
+ </aside>
364
+ `
365
+ : ""
366
+ }
367
+
368
+ <section class="structure-view__detail">
369
+ ${renderStructureWorkspaceHeader(structure, state.structure.selectedName)}
370
+ ${
371
+ state.structure.loading && !structure
372
+ ? `
373
+ <div class="flex min-h-0 flex-1 items-center justify-center border-t border-outline-variant/10 bg-surface-container-low">
374
+ <div class="text-center text-on-surface-variant/40">
375
+ <span class="material-symbols-outlined mb-3 text-4xl">progress_activity</span>
376
+ <p class="font-mono text-[10px] uppercase tracking-[0.22em]">LOADING_STRUCTURE</p>
377
+ </div>
378
+ </div>
379
+ `
380
+ : state.structure.error
381
+ ? `
382
+ <div class="min-h-0 flex-1 border-t border-error/20 bg-error-container/10 px-6 py-5 text-sm text-on-surface">
383
+ <div class="font-headline text-xs font-bold uppercase tracking-[0.18em] text-error">
384
+ ${escapeHtml(state.structure.error.code)}
385
+ </div>
386
+ <div class="mt-2">${escapeHtml(state.structure.error.message)}</div>
387
+ </div>
388
+ `
389
+ : structure
390
+ ? `
391
+ <div class="structure-view__graph-shell">
347
392
  ${renderGraphSurface(
348
393
  structure,
349
394
  state.structure.selectedName,
350
395
  detail,
351
- state.structure.detailLoading
396
+ state.structure.detailLoading,
397
+ tablesVisible
352
398
  )}
353
- </section>
399
+ </div>
354
400
  `
355
401
  : ""
356
402
  }
357
- </div>
403
+ </section>
358
404
  </section>
359
405
  `,
360
406
  panel: "",
@@ -26,8 +26,13 @@ export function renderTableDesignerView(state) {
26
26
  <div class="table-designer-workspace__top">
27
27
  ${renderTableDesignerEditor(state)}
28
28
  </div>
29
- <div class="table-designer-workspace__bottom">
30
- ${renderTableDesignerSqlPreview(state.tableDesigner.draft)}
29
+ <div class="table-designer-workspace__bottom${
30
+ state.tableDesigner.sqlPreviewVisible ? "" : " is-collapsed"
31
+ }">
32
+ ${renderTableDesignerSqlPreview(
33
+ state.tableDesigner.draft,
34
+ state.tableDesigner.sqlPreviewVisible
35
+ )}
31
36
  </div>
32
37
  </div>
33
38
  </section>
@@ -28,6 +28,68 @@ button {
28
28
  cursor: pointer;
29
29
  }
30
30
 
31
+ .control-button {
32
+ align-items: center;
33
+ box-sizing: border-box;
34
+ display: inline-flex;
35
+ gap: var(--spacing-2);
36
+ height: var(--control-height);
37
+ line-height: 1;
38
+ min-height: var(--control-height);
39
+ padding: 0 var(--control-padding-inline);
40
+ }
41
+
42
+ .control-input,
43
+ .control-select {
44
+ box-sizing: border-box;
45
+ height: var(--control-height);
46
+ min-height: var(--control-height);
47
+ }
48
+
49
+ .control-input {
50
+ padding: 0 var(--control-padding-inline);
51
+ }
52
+
53
+ input.control-input--ghost {
54
+ appearance: none;
55
+ background: transparent !important;
56
+ background-color: transparent !important;
57
+ background-image: none !important;
58
+ border: 0 !important;
59
+ border-color: transparent !important;
60
+ box-shadow: none !important;
61
+ -webkit-appearance: none;
62
+ padding: 0;
63
+ }
64
+
65
+ input.control-input--ghost:focus,
66
+ input.control-input--ghost:not(:focus) {
67
+ background: transparent !important;
68
+ background-color: transparent !important;
69
+ border-color: transparent !important;
70
+ box-shadow: none !important;
71
+ outline: none;
72
+ }
73
+
74
+ .control-select {
75
+ padding: 0 var(--control-padding-inline);
76
+ }
77
+
78
+ .control-shell {
79
+ box-sizing: border-box;
80
+ min-height: var(--control-height);
81
+ }
82
+
83
+ .control-icon-button {
84
+ align-items: center;
85
+ box-sizing: border-box;
86
+ display: inline-flex;
87
+ height: var(--control-height);
88
+ justify-content: center;
89
+ min-height: var(--control-height);
90
+ width: var(--control-height);
91
+ }
92
+
31
93
  a {
32
94
  color: inherit;
33
95
  text-decoration: none;