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
|
@@ -67,6 +67,7 @@ function renderTableList(state) {
|
|
|
67
67
|
|
|
68
68
|
function renderWorkspaceHeader(state) {
|
|
69
69
|
const table = state.dataBrowser.table;
|
|
70
|
+
const tablesVisible = state.dataBrowser.tablesVisible !== false;
|
|
70
71
|
|
|
71
72
|
return `
|
|
72
73
|
<header class="border-b border-outline-variant/10 bg-surface-container px-6 py-5">
|
|
@@ -91,19 +92,24 @@ function renderWorkspaceHeader(state) {
|
|
|
91
92
|
<div class="flex items-center gap-3">
|
|
92
93
|
${
|
|
93
94
|
table
|
|
94
|
-
?
|
|
95
|
+
? `<button
|
|
96
|
+
class="standard-button"
|
|
97
|
+
data-action="toggle-data-tables"
|
|
98
|
+
type="button"
|
|
99
|
+
>
|
|
100
|
+
${tablesVisible ? '<span class="material-symbols-outlined text-sm">visibility_off</span> Hide Tables' : 'Show Tables'}
|
|
101
|
+
</button>
|
|
95
102
|
<button
|
|
96
|
-
class="
|
|
103
|
+
class="standard-button"
|
|
97
104
|
data-action="export-data-csv"
|
|
98
105
|
type="button"
|
|
99
106
|
>
|
|
100
107
|
${state.dataBrowser.exportLoading ? 'Exporting...' : 'Export CSV'}
|
|
101
|
-
</button
|
|
102
|
-
`
|
|
108
|
+
</button>`
|
|
103
109
|
: ''
|
|
104
110
|
}
|
|
105
111
|
<button
|
|
106
|
-
class="
|
|
112
|
+
class="standard-button"
|
|
107
113
|
data-action="refresh-view"
|
|
108
114
|
type="button"
|
|
109
115
|
>
|
|
@@ -113,11 +119,12 @@ function renderWorkspaceHeader(state) {
|
|
|
113
119
|
table
|
|
114
120
|
? `
|
|
115
121
|
<button
|
|
116
|
-
class="
|
|
122
|
+
class="standard-button"
|
|
117
123
|
data-action="navigate"
|
|
118
124
|
data-to="/structure"
|
|
119
125
|
type="button"
|
|
120
126
|
>
|
|
127
|
+
<span class="material-symbols-outlined">account_tree</span>
|
|
121
128
|
Open Structure
|
|
122
129
|
</button>
|
|
123
130
|
`
|
|
@@ -233,10 +240,10 @@ function renderTableSearchBar(table, state, activeColumn, filteredRowCount) {
|
|
|
233
240
|
|
|
234
241
|
return `
|
|
235
242
|
<div class="flex flex-wrap items-center gap-3 border-b border-outline-variant/10 bg-surface-container-low px-6 py-4">
|
|
236
|
-
<label class="flex min-w-[18rem] flex-1 items-center gap-3 border border-outline-variant/20 bg-surface-container-lowest px-
|
|
243
|
+
<label class="control-shell flex min-w-[18rem] flex-1 items-center gap-3 border border-outline-variant/20 bg-surface-container-lowest px-3">
|
|
237
244
|
<span class="material-symbols-outlined text-base text-on-surface-variant/55">search</span>
|
|
238
245
|
<input
|
|
239
|
-
class="min-w-0 flex-1
|
|
246
|
+
class="control-input control-input--ghost min-w-0 flex-1 text-sm text-on-surface outline-none placeholder:text-on-surface-variant/40"
|
|
240
247
|
data-bind="data-search-query"
|
|
241
248
|
placeholder="Filter current page..."
|
|
242
249
|
type="search"
|
|
@@ -244,7 +251,7 @@ function renderTableSearchBar(table, state, activeColumn, filteredRowCount) {
|
|
|
244
251
|
/>
|
|
245
252
|
</label>
|
|
246
253
|
<select
|
|
247
|
-
class="min-w-[14rem] border border-outline-variant/20 bg-surface-container-lowest
|
|
254
|
+
class="control-select min-w-[14rem] border border-outline-variant/20 bg-surface-container-lowest font-mono text-xs tracking-[0.04em] text-on-surface outline-none"
|
|
248
255
|
data-bind="data-search-column"
|
|
249
256
|
>
|
|
250
257
|
${columns
|
|
@@ -384,10 +391,8 @@ function renderTableSurface(state) {
|
|
|
384
391
|
.map(
|
|
385
392
|
pageSize => `
|
|
386
393
|
<button
|
|
387
|
-
class="
|
|
388
|
-
pageSize === (table.limit ?? state.dataBrowser.pageSize)
|
|
389
|
-
? 'border-primary-container/30 bg-surface-container-high text-primary-container'
|
|
390
|
-
: 'border-outline-variant/20 text-on-surface hover:bg-surface-container-highest'
|
|
394
|
+
class="standard-button ${
|
|
395
|
+
pageSize === (table.limit ?? state.dataBrowser.pageSize) ? 'is-active' : ''
|
|
391
396
|
}"
|
|
392
397
|
data-action="set-data-page-size"
|
|
393
398
|
data-page-size="${pageSize}"
|
|
@@ -402,7 +407,7 @@ function renderTableSurface(state) {
|
|
|
402
407
|
</div>
|
|
403
408
|
<div class="flex items-center gap-2">
|
|
404
409
|
<button
|
|
405
|
-
class="
|
|
410
|
+
class="standard-button"
|
|
406
411
|
data-action="set-data-page"
|
|
407
412
|
data-page="${page - 1}"
|
|
408
413
|
type="button"
|
|
@@ -414,7 +419,7 @@ function renderTableSurface(state) {
|
|
|
414
419
|
page ${escapeHtml(formatNumber(page))} / ${escapeHtml(formatNumber(pageCount))}
|
|
415
420
|
</div>
|
|
416
421
|
<button
|
|
417
|
-
class="
|
|
422
|
+
class="standard-button"
|
|
418
423
|
data-action="set-data-page"
|
|
419
424
|
data-page="${page + 1}"
|
|
420
425
|
type="button"
|
|
@@ -505,21 +510,29 @@ function renderDataRowEditorPanel(state) {
|
|
|
505
510
|
}
|
|
506
511
|
|
|
507
512
|
export function renderDataView(state) {
|
|
513
|
+
const tablesVisible = state.dataBrowser.tablesVisible !== false;
|
|
514
|
+
|
|
508
515
|
return {
|
|
509
516
|
main: `
|
|
510
517
|
<section class="view-surface min-h-full bg-surface-container flex h-full min-h-0 flex-col overflow-hidden">
|
|
511
|
-
<div class="grid h-full min-h-0 grid-cols-1 md:grid-cols-[18rem_minmax(0,1fr)]">
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
518
|
+
<div class="grid h-full min-h-0 grid-cols-1 ${tablesVisible ? 'md:grid-cols-[18rem_minmax(0,1fr)]' : ''}">
|
|
519
|
+
${
|
|
520
|
+
tablesVisible
|
|
521
|
+
? `
|
|
522
|
+
<aside class="flex min-h-0 flex-col border-r border-outline-variant/10 bg-surface-low">
|
|
523
|
+
<div class="border-b border-outline-variant/10 px-6 py-5">
|
|
524
|
+
<div class="text-[10px] font-bold uppercase tracking-[0.2em] text-primary-container">
|
|
525
|
+
Tables
|
|
526
|
+
</div>
|
|
527
|
+
<div class="mt-2 text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
|
|
528
|
+
total ${escapeHtml(formatNumber(state.dataBrowser.tables.length))}
|
|
529
|
+
</div>
|
|
530
|
+
</div>
|
|
531
|
+
${renderTableList(state)}
|
|
532
|
+
</aside>
|
|
533
|
+
`
|
|
534
|
+
: ''
|
|
535
|
+
}
|
|
523
536
|
<section class="flex min-h-0 flex-col overflow-hidden">
|
|
524
537
|
${renderWorkspaceHeader(state)}
|
|
525
538
|
${renderWorkspaceError(state)}
|
|
@@ -236,7 +236,7 @@ function renderResultsSurface(state, isResultsRoute) {
|
|
|
236
236
|
|
|
237
237
|
export function renderEditorView(state, { isResultsRoute = false } = {}) {
|
|
238
238
|
const connection = getCurrentConnection(state);
|
|
239
|
-
const editorSectionClass = "min-h-[27.5%]";
|
|
239
|
+
const editorSectionClass = state.editor.editorPanelVisible ? "min-h-[27.5%]" : "flex-none";
|
|
240
240
|
const resultsSectionClass = "flex-1";
|
|
241
241
|
|
|
242
242
|
return {
|
|
@@ -252,6 +252,8 @@ export function renderEditorView(state, { isResultsRoute = false } = {}) {
|
|
|
252
252
|
exporting: state.editor.exportLoading,
|
|
253
253
|
historyLoading: state.editor.historyLoading,
|
|
254
254
|
historyTotal: state.editor.historyTotal,
|
|
255
|
+
editorVisible: state.editor.editorPanelVisible,
|
|
256
|
+
historyVisible: state.editor.historyPanelVisible,
|
|
255
257
|
title: connection?.label ?? "SQLite Query Workspace",
|
|
256
258
|
})}
|
|
257
259
|
</section>
|
|
@@ -259,18 +261,22 @@ export function renderEditorView(state, { isResultsRoute = false } = {}) {
|
|
|
259
261
|
${renderResultsSurface(state, isResultsRoute)}
|
|
260
262
|
</section>
|
|
261
263
|
</div>
|
|
262
|
-
${
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
264
|
+
${
|
|
265
|
+
state.editor.historyPanelVisible
|
|
266
|
+
? renderQueryHistoryPanel({
|
|
267
|
+
items: state.editor.history,
|
|
268
|
+
loading: state.editor.historyLoading,
|
|
269
|
+
loadingMore: state.editor.historyLoadingMore,
|
|
270
|
+
error: state.editor.historyError,
|
|
271
|
+
activeTab: state.editor.historyTab,
|
|
272
|
+
search: state.editor.historySearchInput,
|
|
273
|
+
total: state.editor.historyTotal,
|
|
274
|
+
hasMore: state.editor.historyHasMore,
|
|
275
|
+
activeHistoryId: state.editor.historyActiveId,
|
|
276
|
+
selectedHistoryId: state.editor.historySelectedId,
|
|
277
|
+
})
|
|
278
|
+
: ""
|
|
279
|
+
}
|
|
274
280
|
</section>
|
|
275
281
|
`,
|
|
276
282
|
panel:
|
|
@@ -58,7 +58,7 @@ function renderTopTables(overview) {
|
|
|
58
58
|
);
|
|
59
59
|
|
|
60
60
|
return `
|
|
61
|
-
<section class="shell-section
|
|
61
|
+
<section class="shell-section">
|
|
62
62
|
<div class="flex items-center justify-between bg-surface-container-highest px-4 py-2">
|
|
63
63
|
<span class="text-[10px] font-bold uppercase tracking-[0.25em]">Top Tables</span>
|
|
64
64
|
<span class="material-symbols-outlined text-xs text-on-surface-variant">table_rows</span>
|
|
@@ -131,7 +131,7 @@ function renderOperationalSurface(overview) {
|
|
|
131
131
|
? `
|
|
132
132
|
<div class="border-t border-outline-variant/10 px-4 py-3">
|
|
133
133
|
<button
|
|
134
|
-
class="
|
|
134
|
+
class="standard-button"
|
|
135
135
|
data-action="open-overview-in-finder"
|
|
136
136
|
type="button"
|
|
137
137
|
>
|
|
@@ -145,17 +145,154 @@ function renderOperationalSurface(overview) {
|
|
|
145
145
|
`;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
function pluralize(count, singular, plural = `${singular}s`) {
|
|
149
|
+
return Number(count) === 1 ? singular : plural;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function getSchemaMapNarrative(preview) {
|
|
153
|
+
const relationshipCount = Number(preview.relationshipCount ?? 0);
|
|
154
|
+
const fkClusters = Number(preview.fkClusters ?? 0);
|
|
155
|
+
const isolatedTables = Number(preview.isolatedTables ?? 0);
|
|
156
|
+
const connectedTables = Math.max(0, Number(preview.tableCount ?? 0) - isolatedTables);
|
|
157
|
+
|
|
158
|
+
if (relationshipCount === 0 || fkClusters === 0) {
|
|
159
|
+
return {
|
|
160
|
+
title: "Mostly standalone schema",
|
|
161
|
+
body:
|
|
162
|
+
"There are no meaningful foreign-key groups yet. The Structure view will read more like a list of isolated tables than a connected graph.",
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (isolatedTables === 0) {
|
|
167
|
+
return {
|
|
168
|
+
title: "Connected schema",
|
|
169
|
+
body: `Foreign keys tie the whole schema together in ${formatNumber(
|
|
170
|
+
fkClusters
|
|
171
|
+
)} ${pluralize(fkClusters, "cluster")}. Structure is useful here because dependencies and joins are visible at a glance.`,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
title: "One connected core, several islands",
|
|
177
|
+
body: `${formatNumber(connectedTables)} ${pluralize(
|
|
178
|
+
connectedTables,
|
|
179
|
+
"table"
|
|
180
|
+
)} are connected through foreign keys, while ${formatNumber(
|
|
181
|
+
isolatedTables
|
|
182
|
+
)} ${pluralize(
|
|
183
|
+
isolatedTables,
|
|
184
|
+
"table"
|
|
185
|
+
)} stand alone. Structure helps most when you want to inspect the connected core and ignore the isolated tables.`,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function renderSchemaMapPreview(overview) {
|
|
190
|
+
const preview = overview.schemaMap ?? {
|
|
191
|
+
tableCount: overview.counts?.tables ?? 0,
|
|
192
|
+
indexCount: overview.counts?.indexes ?? 0,
|
|
193
|
+
relationshipCount: 0,
|
|
194
|
+
fkClusters: 0,
|
|
195
|
+
isolatedTables: 0,
|
|
196
|
+
};
|
|
197
|
+
const narrative = getSchemaMapNarrative(preview);
|
|
198
|
+
const stats = [
|
|
199
|
+
{ label: "FK Links", value: formatNumber(preview.relationshipCount) },
|
|
200
|
+
{ label: "FK Clusters", value: formatNumber(preview.fkClusters) },
|
|
201
|
+
{ label: "Isolated Tables", value: formatNumber(preview.isolatedTables) },
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
return `
|
|
205
|
+
<section class="shell-section overflow-hidden bg-[radial-gradient(circle_at_top_left,rgba(45,250,255,0.12),transparent_34%),radial-gradient(circle_at_bottom_right,rgba(252,227,0,0.12),transparent_42%),linear-gradient(135deg,#1b1b19,#101111)]">
|
|
206
|
+
<div class="flex items-center justify-between border-b border-outline-variant/10 bg-surface-container-highest/60 px-4 py-2">
|
|
207
|
+
<span class="text-[10px] font-bold uppercase tracking-[0.25em]">Schema Map</span>
|
|
208
|
+
<span class="material-symbols-outlined text-xs text-on-surface-variant">account_tree</span>
|
|
209
|
+
</div>
|
|
210
|
+
<div class="space-y-5 p-4">
|
|
211
|
+
<div class="flex flex-col gap-5 xl:flex-row xl:items-end xl:justify-between">
|
|
212
|
+
<div>
|
|
213
|
+
<div class="font-headline text-xl font-black uppercase tracking-tight text-primary-container">
|
|
214
|
+
${escapeHtml(narrative.title)}
|
|
215
|
+
</div>
|
|
216
|
+
<p class="mt-2 max-w-3xl text-sm leading-6 text-on-surface-variant/70">
|
|
217
|
+
${escapeHtml(narrative.body)}
|
|
218
|
+
</p>
|
|
219
|
+
</div>
|
|
220
|
+
<div class="flex justify-start xl:justify-end">
|
|
221
|
+
<button
|
|
222
|
+
class="standard-button"
|
|
223
|
+
data-action="navigate"
|
|
224
|
+
data-to="/structure"
|
|
225
|
+
type="button"
|
|
226
|
+
>
|
|
227
|
+
<span class="material-symbols-outlined text-sm">account_tree</span>
|
|
228
|
+
Open Structure
|
|
229
|
+
</button>
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
<div class="grid grid-cols-1 gap-3 sm:grid-cols-3">
|
|
233
|
+
${stats
|
|
234
|
+
.map(
|
|
235
|
+
(stat) => `
|
|
236
|
+
<div class="border border-outline-variant/10 bg-surface-container-lowest/70 px-3 py-3">
|
|
237
|
+
<div class="text-[9px] font-mono uppercase tracking-[0.18em] text-on-surface-variant/55">
|
|
238
|
+
${escapeHtml(stat.label)}
|
|
239
|
+
</div>
|
|
240
|
+
<div class="mt-2 font-headline text-2xl font-black uppercase tracking-tight text-on-surface">
|
|
241
|
+
${escapeHtml(stat.value)}
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
`
|
|
245
|
+
)
|
|
246
|
+
.join("")}
|
|
247
|
+
</div>
|
|
248
|
+
<div class="border border-outline-variant/10 bg-surface-container-lowest/70 px-4 py-4">
|
|
249
|
+
<div class="text-[10px] font-mono uppercase tracking-[0.18em] text-primary-container/70">
|
|
250
|
+
Why It Helps
|
|
251
|
+
</div>
|
|
252
|
+
<div class="mt-3 grid gap-3 md:grid-cols-3">
|
|
253
|
+
<div class="border border-outline-variant/10 bg-surface-container px-3 py-3">
|
|
254
|
+
<div class="text-[9px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
|
|
255
|
+
FK Links
|
|
256
|
+
</div>
|
|
257
|
+
<div class="mt-2 text-sm leading-6 text-on-surface-variant/70">
|
|
258
|
+
Shows how many actual relationships exist between tables.
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
<div class="border border-outline-variant/10 bg-surface-container px-3 py-3">
|
|
262
|
+
<div class="text-[9px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
|
|
263
|
+
FK Clusters
|
|
264
|
+
</div>
|
|
265
|
+
<div class="mt-2 text-sm leading-6 text-on-surface-variant/70">
|
|
266
|
+
Tells you whether the schema is one connected area or split into separate groups.
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
<div class="border border-outline-variant/10 bg-surface-container px-3 py-3">
|
|
270
|
+
<div class="text-[9px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
|
|
271
|
+
Isolated Tables
|
|
272
|
+
</div>
|
|
273
|
+
<div class="mt-2 text-sm leading-6 text-on-surface-variant/70">
|
|
274
|
+
Highlights how many tables have no FK links and can usually be ignored during relationship analysis.
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
</div>
|
|
278
|
+
</div>
|
|
279
|
+
</div>
|
|
280
|
+
</section>
|
|
281
|
+
`;
|
|
282
|
+
}
|
|
283
|
+
|
|
148
284
|
function renderIntegrityCard(overview, readOnly) {
|
|
149
285
|
return `
|
|
150
|
-
<section class="relative overflow-hidden
|
|
151
|
-
<div class="
|
|
286
|
+
<section class="shell-section relative overflow-hidden bg-[radial-gradient(circle_at_top_right,rgba(252,227,0,0.14),transparent_30%),linear-gradient(135deg,#1c1b1b,#0e0e0e)]">
|
|
287
|
+
<div class="flex items-center justify-between border-b border-outline-variant/10 bg-surface-container-highest/60 px-4 py-2">
|
|
288
|
+
<span class="text-[10px] font-bold uppercase tracking-[0.25em]">Integrity Status</span>
|
|
289
|
+
<span class="material-symbols-outlined text-xs text-on-surface-variant">security</span>
|
|
290
|
+
</div>
|
|
291
|
+
<div class="pointer-events-none absolute inset-x-0 bottom-0 top-10 bg-[linear-gradient(to_top,rgba(32,31,31,0.95),rgba(32,31,31,0.2))]"></div>
|
|
152
292
|
<div class="relative flex min-h-[220px] flex-col justify-end p-6">
|
|
153
293
|
<div class="mb-6 flex h-16 w-16 items-center justify-center border border-primary-container/20 bg-primary-container/10">
|
|
154
294
|
<span class="material-symbols-outlined text-4xl text-primary-container">security</span>
|
|
155
295
|
</div>
|
|
156
|
-
<div class="text-[10px] font-black uppercase tracking-[0.25em] text-primary-container">
|
|
157
|
-
INTEGRITY_STATUS
|
|
158
|
-
</div>
|
|
159
296
|
<div class="mt-2 flex flex-wrap items-center gap-2">
|
|
160
297
|
${renderStatusBadge(overview.sqlite?.integrityCheck ?? "unknown", "success")}
|
|
161
298
|
${renderStatusBadge(
|
|
@@ -190,10 +327,9 @@ export function renderOverviewView(state) {
|
|
|
190
327
|
: "System Registry: NO_ACTIVE_DATABASE",
|
|
191
328
|
actions: `
|
|
192
329
|
<button
|
|
193
|
-
class="
|
|
330
|
+
class="signature-button"
|
|
194
331
|
data-action="navigate"
|
|
195
332
|
data-to="/editor"
|
|
196
|
-
style="--clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 0 100%);"
|
|
197
333
|
type="button"
|
|
198
334
|
>
|
|
199
335
|
<span class="material-symbols-outlined text-sm">terminal</span>
|
|
@@ -218,7 +354,10 @@ export function renderOverviewView(state) {
|
|
|
218
354
|
? `
|
|
219
355
|
${renderOverviewMetrics(overview)}
|
|
220
356
|
<div class="grid grid-cols-1 gap-6 xl:grid-cols-3">
|
|
221
|
-
|
|
357
|
+
<div class="space-y-6 xl:col-span-2">
|
|
358
|
+
${renderTopTables(overview)}
|
|
359
|
+
${renderSchemaMapPreview(overview)}
|
|
360
|
+
</div>
|
|
222
361
|
<div class="space-y-6">
|
|
223
362
|
${renderOperationalSurface(overview)}
|
|
224
363
|
${renderIntegrityCard(overview, readOnly)}
|
|
@@ -56,7 +56,7 @@ function renderSettingsContent(state) {
|
|
|
56
56
|
Copyright Oliver Jessner
|
|
57
57
|
</div>
|
|
58
58
|
<a
|
|
59
|
-
class="
|
|
59
|
+
class="standard-button"
|
|
60
60
|
href="https://oliverjessner.at"
|
|
61
61
|
rel="noreferrer"
|
|
62
62
|
target="_blank"
|
|
@@ -77,7 +77,7 @@ function renderSettingsContent(state) {
|
|
|
77
77
|
Open Source Code
|
|
78
78
|
</div>
|
|
79
79
|
<a
|
|
80
|
-
class="
|
|
80
|
+
class="standard-button"
|
|
81
81
|
href="https://github.com/oliverjessner/sqlite-hub"
|
|
82
82
|
rel="noreferrer"
|
|
83
83
|
target="_blank"
|