sqlite-hub 0.7.0 → 0.9.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.
- package/changelog.md +25 -0
- package/docs/DESIGN_GUIDELINES.md +36 -0
- package/frontend/index.html +79 -0
- package/frontend/js/api.js +67 -0
- package/frontend/js/app.js +1401 -921
- package/frontend/js/components/connectionCard.js +3 -4
- package/frontend/js/components/emptyState.js +4 -4
- package/frontend/js/components/modal.js +239 -30
- package/frontend/js/components/queryEditor.js +11 -8
- package/frontend/js/components/queryHistoryDetail.js +27 -8
- package/frontend/js/components/queryHistoryPanel.js +6 -5
- package/frontend/js/components/rowEditorPanel.js +84 -58
- package/frontend/js/components/sidebar.js +76 -33
- package/frontend/js/components/structureGraph.js +629 -715
- package/frontend/js/components/tableDesignerEditor.js +9 -8
- package/frontend/js/components/tableDesignerSidebar.js +11 -10
- package/frontend/js/components/tableDesignerSqlPreview.js +61 -30
- package/frontend/js/lib/mediaTaggingDefaults.js +27 -0
- package/frontend/js/router.js +81 -71
- package/frontend/js/store.js +3095 -2165
- package/frontend/js/views/charts.js +68 -40
- package/frontend/js/views/connections.js +5 -17
- package/frontend/js/views/data.js +40 -27
- package/frontend/js/views/editor.js +172 -177
- package/frontend/js/views/mediaTagging.js +861 -0
- package/frontend/js/views/overview.js +149 -10
- package/frontend/js/views/settings.js +2 -2
- package/frontend/js/views/structure.js +74 -70
- package/frontend/js/views/tableDesigner.js +7 -2
- package/frontend/styles/base.css +73 -1
- package/frontend/styles/components.css +105 -105
- package/frontend/styles/structure-graph.css +19 -82
- package/frontend/styles/tokens.css +2 -0
- package/frontend/styles/views.css +823 -30
- package/package.json +1 -1
- package/server/routes/charts.js +4 -1
- package/server/routes/data.js +14 -0
- package/server/routes/mediaTagging.js +166 -0
- package/server/routes/sql.js +1 -0
- package/server/server.js +4 -0
- package/server/services/sqlite/dataBrowserService.js +25 -0
- package/server/services/sqlite/exportService.js +31 -1
- package/server/services/sqlite/mediaTaggingService.js +1689 -0
- package/server/services/sqlite/overviewService.js +68 -0
- package/server/services/storage/appStateStore.js +321 -2
|
@@ -20,7 +20,7 @@ function getQueryTypeTone(queryType, isDestructive) {
|
|
|
20
20
|
return "muted";
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
function
|
|
23
|
+
export function renderQueryHistoryListItem(item, activeHistoryId, selectedHistoryId) {
|
|
24
24
|
const isActive = Number(activeHistoryId) === Number(item.id);
|
|
25
25
|
const isSelected = Number(selectedHistoryId) === Number(item.id);
|
|
26
26
|
const visibleTables = (item.tablesDetected ?? []).slice(0, 3);
|
|
@@ -109,6 +109,7 @@ function renderQueryHistoryTabs(activeTab, historyTotal) {
|
|
|
109
109
|
const tabs = [
|
|
110
110
|
{ id: "recent", label: "Recent" },
|
|
111
111
|
{ id: "saved", label: "Saved" },
|
|
112
|
+
{ id: "unsaved", label: "Unsaved" },
|
|
112
113
|
{ id: "failed", label: "Failed" },
|
|
113
114
|
];
|
|
114
115
|
|
|
@@ -171,7 +172,7 @@ export function renderQueryHistoryPanel({
|
|
|
171
172
|
<label class="mt-4 block">
|
|
172
173
|
<span class="sr-only">Search query history</span>
|
|
173
174
|
<input
|
|
174
|
-
class="w-full border border-outline-variant/20 bg-surface-container
|
|
175
|
+
class="control-input w-full border border-outline-variant/20 bg-surface-container text-sm text-on-surface outline-none placeholder:text-on-surface-variant/35 focus:border-primary-container"
|
|
175
176
|
data-bind="query-history-search"
|
|
176
177
|
placeholder="Search SQL, titles, notes..."
|
|
177
178
|
type="search"
|
|
@@ -204,9 +205,9 @@ export function renderQueryHistoryPanel({
|
|
|
204
205
|
`
|
|
205
206
|
: ""
|
|
206
207
|
}
|
|
207
|
-
|
|
208
|
+
<div class="space-y-3">
|
|
208
209
|
${items
|
|
209
|
-
.map((item) =>
|
|
210
|
+
.map((item) => renderQueryHistoryListItem(item, activeHistoryId, selectedHistoryId))
|
|
210
211
|
.join("")}
|
|
211
212
|
</div>
|
|
212
213
|
${
|
|
@@ -223,7 +224,7 @@ export function renderQueryHistoryPanel({
|
|
|
223
224
|
? `
|
|
224
225
|
<div class="mt-4 flex justify-center">
|
|
225
226
|
<button
|
|
226
|
-
class="
|
|
227
|
+
class="standard-button"
|
|
227
228
|
data-action="load-more-query-history"
|
|
228
229
|
type="button"
|
|
229
230
|
>
|
|
@@ -42,12 +42,23 @@ function renderJsonViewer(prettyJson, title = "JSON Viewer") {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
function renderReadonlyField(label, value) {
|
|
45
|
+
const badges = Array.isArray(label?.badges) ? label.badges : [];
|
|
46
|
+
const displayLabel = typeof label === "object" ? label.label : label;
|
|
45
47
|
const jsonPreview = getJsonPreview(value);
|
|
46
48
|
|
|
47
49
|
return `
|
|
48
50
|
<div class="border border-outline-variant/10 bg-surface-container-lowest px-4 py-3">
|
|
49
|
-
<div class="text-[10px] font-mono uppercase tracking-[0.18em] text-on-surface-variant/55">
|
|
50
|
-
|
|
51
|
+
<div class="flex flex-wrap items-center gap-2 text-[10px] font-mono uppercase tracking-[0.18em] text-on-surface-variant/55">
|
|
52
|
+
<span>${escapeHtml(displayLabel)}</span>
|
|
53
|
+
${badges
|
|
54
|
+
.map(
|
|
55
|
+
(badge) => `
|
|
56
|
+
<span class="border border-outline-variant/20 bg-surface-container px-2 py-1 text-[9px] text-primary-container">
|
|
57
|
+
${escapeHtml(badge)}
|
|
58
|
+
</span>
|
|
59
|
+
`
|
|
60
|
+
)
|
|
61
|
+
.join("")}
|
|
51
62
|
</div>
|
|
52
63
|
${
|
|
53
64
|
jsonPreview
|
|
@@ -59,12 +70,22 @@ function renderReadonlyField(label, value) {
|
|
|
59
70
|
}
|
|
60
71
|
|
|
61
72
|
function renderEditableField(field) {
|
|
73
|
+
const badges = Array.isArray(field.badges) ? field.badges : [];
|
|
62
74
|
const jsonPreview = getJsonPreview(field.value);
|
|
63
75
|
|
|
64
76
|
return `
|
|
65
77
|
<label class="block space-y-2">
|
|
66
|
-
<span class="text-[10px] font-mono uppercase tracking-[0.18em] text-on-surface-variant/55">
|
|
67
|
-
|
|
78
|
+
<span class="flex flex-wrap items-center gap-2 text-[10px] font-mono uppercase tracking-[0.18em] text-on-surface-variant/55">
|
|
79
|
+
<span>${escapeHtml(field.label ?? field.name)}</span>
|
|
80
|
+
${badges
|
|
81
|
+
.map(
|
|
82
|
+
(badge) => `
|
|
83
|
+
<span class="border border-outline-variant/20 bg-surface-container px-2 py-1 text-[9px] text-primary-container">
|
|
84
|
+
${escapeHtml(badge)}
|
|
85
|
+
</span>
|
|
86
|
+
`
|
|
87
|
+
)
|
|
88
|
+
.join("")}
|
|
68
89
|
</span>
|
|
69
90
|
${
|
|
70
91
|
jsonPreview
|
|
@@ -106,12 +127,52 @@ export function renderRowEditorPanel({
|
|
|
106
127
|
const canSubmit = !disabledMessage && editableFields.length > 0;
|
|
107
128
|
const canDelete = !disabledMessage && deleteEnabled;
|
|
108
129
|
const formId = `${formName}-panel-form`;
|
|
130
|
+
const headerActions = [
|
|
131
|
+
reloadAction
|
|
132
|
+
? `
|
|
133
|
+
<button
|
|
134
|
+
class="standard-button"
|
|
135
|
+
data-action="${escapeHtml(reloadAction)}"
|
|
136
|
+
type="button"
|
|
137
|
+
>
|
|
138
|
+
Reload
|
|
139
|
+
</button>
|
|
140
|
+
`
|
|
141
|
+
: "",
|
|
142
|
+
canSubmit
|
|
143
|
+
? `
|
|
144
|
+
<button
|
|
145
|
+
class="standard-button"
|
|
146
|
+
form="${escapeHtml(formId)}"
|
|
147
|
+
type="submit"
|
|
148
|
+
${saving || deleting ? "disabled" : ""}
|
|
149
|
+
>
|
|
150
|
+
${escapeHtml(saving ? "Saving..." : submitLabel)}
|
|
151
|
+
</button>
|
|
152
|
+
`
|
|
153
|
+
: "",
|
|
154
|
+
canDelete
|
|
155
|
+
? `
|
|
156
|
+
<button
|
|
157
|
+
class="delete-button"
|
|
158
|
+
data-action="${escapeHtml(deleteAction)}"
|
|
159
|
+
${deleteRowIndex === null ? "" : `data-row-index="${escapeHtml(String(deleteRowIndex))}"`}
|
|
160
|
+
type="button"
|
|
161
|
+
${saving || deleting ? "disabled" : ""}
|
|
162
|
+
>
|
|
163
|
+
${escapeHtml(deleting ? "Deleting..." : deleteLabel)}
|
|
164
|
+
</button>
|
|
165
|
+
`
|
|
166
|
+
: "",
|
|
167
|
+
]
|
|
168
|
+
.filter(Boolean)
|
|
169
|
+
.join("");
|
|
109
170
|
|
|
110
171
|
return `
|
|
111
172
|
<section class="flex h-full min-h-0 flex-col bg-surface-low">
|
|
112
173
|
<header class="border-b border-outline-variant/10 bg-surface-container px-6 py-5">
|
|
113
|
-
<div class="
|
|
114
|
-
<div>
|
|
174
|
+
<div class="flex items-start justify-between gap-4">
|
|
175
|
+
<div class="min-w-0 flex-1">
|
|
115
176
|
<div class="text-[10px] font-bold uppercase tracking-[0.2em] text-primary-container">
|
|
116
177
|
${escapeHtml(sectionLabel)}
|
|
117
178
|
</div>
|
|
@@ -128,59 +189,24 @@ export function renderRowEditorPanel({
|
|
|
128
189
|
: ""
|
|
129
190
|
}
|
|
130
191
|
</div>
|
|
131
|
-
<
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
>
|
|
140
|
-
Reload
|
|
141
|
-
</button>
|
|
142
|
-
`
|
|
143
|
-
: ""
|
|
144
|
-
}
|
|
145
|
-
${
|
|
146
|
-
canSubmit
|
|
147
|
-
? `
|
|
148
|
-
<button
|
|
149
|
-
class="bg-primary-container px-5 py-3 text-[10px] font-black uppercase tracking-[0.16em] text-on-primary disabled:cursor-default disabled:opacity-40"
|
|
150
|
-
form="${escapeHtml(formId)}"
|
|
151
|
-
type="submit"
|
|
152
|
-
${saving || deleting ? "disabled" : ""}
|
|
153
|
-
>
|
|
154
|
-
${escapeHtml(saving ? "Saving..." : submitLabel)}
|
|
155
|
-
</button>
|
|
156
|
-
`
|
|
157
|
-
: ""
|
|
158
|
-
}
|
|
159
|
-
${
|
|
160
|
-
canDelete
|
|
161
|
-
? `
|
|
162
|
-
<button
|
|
163
|
-
class="border border-error/25 bg-error-container/10 px-4 py-3 text-[10px] font-bold uppercase tracking-[0.16em] text-error transition-colors hover:bg-error-container/20 disabled:cursor-default disabled:opacity-40"
|
|
164
|
-
data-action="${escapeHtml(deleteAction)}"
|
|
165
|
-
data-row-index="${escapeHtml(String(deleteRowIndex ?? ""))}"
|
|
166
|
-
type="button"
|
|
167
|
-
${saving || deleting ? "disabled" : ""}
|
|
168
|
-
>
|
|
169
|
-
${escapeHtml(deleting ? "Deleting..." : deleteLabel)}
|
|
170
|
-
</button>
|
|
171
|
-
`
|
|
172
|
-
: ""
|
|
173
|
-
}
|
|
174
|
-
<button
|
|
175
|
-
aria-label="Close panel"
|
|
176
|
-
class="flex h-11 w-11 items-center justify-center border border-outline-variant/20 text-on-surface hover:bg-surface-container-highest"
|
|
177
|
-
data-action="${escapeHtml(closeAction)}"
|
|
178
|
-
type="button"
|
|
179
|
-
>
|
|
180
|
-
<span class="material-symbols-outlined text-base">close</span>
|
|
181
|
-
</button>
|
|
182
|
-
</div>
|
|
192
|
+
<button
|
|
193
|
+
aria-label="Close panel"
|
|
194
|
+
class="query-history-icon-button shrink-0"
|
|
195
|
+
data-action="${escapeHtml(closeAction)}"
|
|
196
|
+
type="button"
|
|
197
|
+
>
|
|
198
|
+
<span class="material-symbols-outlined text-[18px]">close</span>
|
|
199
|
+
</button>
|
|
183
200
|
</div>
|
|
201
|
+
${
|
|
202
|
+
headerActions
|
|
203
|
+
? `
|
|
204
|
+
<div class="mt-4 flex flex-wrap items-center justify-end gap-2">
|
|
205
|
+
${headerActions}
|
|
206
|
+
</div>
|
|
207
|
+
`
|
|
208
|
+
: ""
|
|
209
|
+
}
|
|
184
210
|
</header>
|
|
185
211
|
<div class="custom-scrollbar flex-1 overflow-auto px-6 py-6">
|
|
186
212
|
${
|
|
@@ -1,61 +1,104 @@
|
|
|
1
|
-
import { escapeHtml } from
|
|
2
|
-
import { renderConnectionLogo } from
|
|
1
|
+
import { escapeHtml } from '../utils/format.js';
|
|
2
|
+
import { renderConnectionLogo } from './connectionLogo.js';
|
|
3
3
|
|
|
4
4
|
const sidebarItems = [
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
{ label: 'Connections', href: '#/connections', key: 'connections', icon: 'database' },
|
|
6
|
+
{ label: 'Overview', href: '#/overview', key: 'overview', icon: 'dashboard' },
|
|
7
|
+
{ label: 'Data', href: '#/data', key: 'data', icon: 'table_rows' },
|
|
8
|
+
{ label: 'Structure', href: '#/structure', key: 'structure', icon: 'account_tree' },
|
|
9
|
+
{ label: 'SQL Editor', href: '#/editor', key: 'editor', icon: 'terminal' },
|
|
10
|
+
{ label: 'Charts', href: '#/charts', key: 'charts', icon: 'bar_chart' },
|
|
11
|
+
{ label: 'Table Designer', href: '#/table-designer', key: 'tableDesigner', icon: 'table_chart' },
|
|
12
|
+
{
|
|
13
|
+
label: 'Media Tagging',
|
|
14
|
+
key: 'mediaTagging',
|
|
15
|
+
icon: 'sell',
|
|
16
|
+
children: [
|
|
17
|
+
{ label: 'Setup', href: '#/media-tagging', key: 'mediaTaggingSetup' },
|
|
18
|
+
{ label: 'Tagging Queue', href: '#/media-tagging/queue', key: 'mediaTaggingQueue' },
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
{ label: 'Settings', href: '#/settings', key: 'settings', icon: 'settings' },
|
|
13
22
|
];
|
|
14
23
|
|
|
15
24
|
function getActiveSidebarKey(routeName) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
25
|
+
if (routeName === 'landing') {
|
|
26
|
+
return 'connections';
|
|
27
|
+
}
|
|
19
28
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
if (routeName === 'editorResults') {
|
|
30
|
+
return 'editor';
|
|
31
|
+
}
|
|
23
32
|
|
|
24
|
-
|
|
33
|
+
if (routeName === 'mediaTaggingSetup' || routeName === 'mediaTaggingQueue') {
|
|
34
|
+
return 'mediaTagging';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return routeName;
|
|
25
38
|
}
|
|
26
39
|
|
|
27
40
|
export function renderSidebar(state) {
|
|
28
|
-
|
|
29
|
-
|
|
41
|
+
const activeKey = getActiveSidebarKey(state.route.name);
|
|
42
|
+
const activeConnection = state.connections.active;
|
|
43
|
+
const expandedKey = activeKey === 'mediaTagging' ? 'mediaTagging' : null;
|
|
30
44
|
|
|
31
|
-
|
|
45
|
+
return `
|
|
32
46
|
<nav class="sidebar-links">
|
|
33
47
|
${sidebarItems
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
48
|
+
.map(item => {
|
|
49
|
+
if (item.children) {
|
|
50
|
+
const isExpanded = expandedKey === item.key;
|
|
51
|
+
const isActive = activeKey === item.key;
|
|
52
|
+
return `
|
|
53
|
+
<div class="sidebar-group">
|
|
54
|
+
<a class="sidebar-link ${isActive ? 'is-active' : ''}" href="${item.children[0].href}" data-group="${item.key}">
|
|
55
|
+
<span class="material-symbols-outlined">${item.icon}</span>
|
|
56
|
+
<span>${item.label}</span>
|
|
57
|
+
<span class="material-symbols-outlined ml-auto text-[14px] ${isExpanded ? 'rotate-180' : ''}">expand_more</span>
|
|
58
|
+
</a>
|
|
59
|
+
${
|
|
60
|
+
isExpanded
|
|
61
|
+
? `
|
|
62
|
+
<div class="sidebar-sublinks">
|
|
63
|
+
${item.children
|
|
64
|
+
.map(
|
|
65
|
+
child => `
|
|
66
|
+
<a class="sidebar-sublink ${state.route.name === child.key ? 'is-active' : ''}" href="${child.href}">
|
|
67
|
+
${child.label}
|
|
68
|
+
</a>
|
|
69
|
+
`,
|
|
70
|
+
)
|
|
71
|
+
.join('')}
|
|
72
|
+
</div>
|
|
73
|
+
`
|
|
74
|
+
: ''
|
|
75
|
+
}
|
|
76
|
+
</div>
|
|
77
|
+
`;
|
|
78
|
+
}
|
|
79
|
+
return `
|
|
80
|
+
<a class="sidebar-link ${item.key === activeKey ? 'is-active' : ''}" href="${item.href}">
|
|
37
81
|
<span class="material-symbols-outlined">${item.icon}</span>
|
|
38
82
|
<span>${item.label}</span>
|
|
39
83
|
</a>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
84
|
+
`;
|
|
85
|
+
})
|
|
86
|
+
.join('')}
|
|
43
87
|
</nav>
|
|
44
88
|
<div class="sidebar-footer">
|
|
45
89
|
<div class="sidebar-footer-card">
|
|
46
90
|
${renderConnectionLogo(activeConnection, {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
icon: "memory",
|
|
91
|
+
containerClass: 'sidebar-footer-mark overflow-hidden bg-primary-container text-on-primary',
|
|
92
|
+
imageClassName: 'h-full w-full object-cover',
|
|
93
|
+
iconClassName: 'text-[15px]',
|
|
94
|
+
icon: 'memory',
|
|
52
95
|
})}
|
|
53
96
|
<div class="min-w-0">
|
|
54
97
|
<p class="truncate text-[10px] font-bold text-on-surface">
|
|
55
|
-
${escapeHtml(activeConnection?.label ??
|
|
98
|
+
${escapeHtml(activeConnection?.label ?? 'NO_ACTIVE_DATABASE')}
|
|
56
99
|
</p>
|
|
57
100
|
<p class="text-[8px] text-on-surface-variant/60">
|
|
58
|
-
${activeConnection?.readOnly ?
|
|
101
|
+
${activeConnection?.readOnly ? 'READ_ONLY' : 'READ_WRITE'}
|
|
59
102
|
</p>
|
|
60
103
|
</div>
|
|
61
104
|
</div>
|