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.
- package/README.md +107 -0
- package/bin/sqlite-hub.js +285 -4
- package/changelog.md +21 -0
- package/docs/DESIGN_GUIDELINES.md +36 -0
- package/frontend/assets/mockups/sql_editor_croped.png +0 -0
- package/frontend/index.html +80 -0
- package/frontend/js/api.js +34 -0
- package/frontend/js/app.js +188 -10
- package/frontend/js/components/connectionCard.js +3 -4
- package/frontend/js/components/emptyState.js +4 -4
- package/frontend/js/components/modal.js +341 -24
- package/frontend/js/components/queryChartRenderer.js +125 -0
- package/frontend/js/components/queryEditor.js +33 -6
- package/frontend/js/components/queryHistoryDetail.js +16 -7
- package/frontend/js/components/queryHistoryPanel.js +18 -7
- package/frontend/js/components/rowEditorPanel.js +59 -54
- package/frontend/js/components/sidebar.js +32 -32
- package/frontend/js/components/structureGraph.js +54 -122
- package/frontend/js/components/tableDesignerEditor.js +9 -8
- package/frontend/js/components/tableDesignerSidebar.js +52 -48
- package/frontend/js/components/tableDesignerSqlPreview.js +60 -28
- package/frontend/js/lib/queryChartOptions.js +283 -0
- package/frontend/js/lib/queryCharts.js +560 -0
- package/frontend/js/router.js +8 -0
- package/frontend/js/store.js +641 -0
- package/frontend/js/views/charts.js +499 -0
- package/frontend/js/views/connections.js +5 -17
- package/frontend/js/views/data.js +40 -27
- package/frontend/js/views/editor.js +19 -13
- package/frontend/js/views/overview.js +149 -10
- package/frontend/js/views/settings.js +2 -2
- package/frontend/js/views/structure.js +105 -59
- package/frontend/js/views/tableDesigner.js +7 -2
- package/frontend/styles/base.css +62 -0
- package/frontend/styles/components.css +68 -118
- package/frontend/styles/structure-graph.css +19 -82
- package/frontend/styles/tokens.css +2 -0
- package/frontend/styles/views.css +293 -66
- package/package.json +2 -1
- package/server/routes/charts.js +153 -0
- package/server/server.js +6 -0
- package/server/services/sqlite/overviewService.js +68 -0
- package/server/services/storage/appStateStore.js +400 -1
- package/server/services/storage/queryHistoryChartUtils.js +145 -0
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import {
|
|
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="
|
|
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
|
-
|
|
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
|
-
<
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
</
|
|
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="
|
|
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="
|
|
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="
|
|
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="
|
|
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="
|
|
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
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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
|
-
|
|
346
|
-
|
|
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
|
-
</
|
|
399
|
+
</div>
|
|
354
400
|
`
|
|
355
401
|
: ""
|
|
356
402
|
}
|
|
357
|
-
</
|
|
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
|
-
|
|
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>
|
package/frontend/styles/base.css
CHANGED
|
@@ -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;
|