sqlite-hub 1.0.0 → 1.1.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/README.md +47 -33
- package/bin/sqlite-hub.js +127 -47
- package/frontend/js/api.js +6 -0
- package/frontend/js/app.js +199 -34
- package/frontend/js/components/modal.js +17 -1
- package/frontend/js/components/queryChartRenderer.js +28 -3
- package/frontend/js/components/queryEditor.js +20 -24
- package/frontend/js/components/queryHistoryDetail.js +1 -1
- package/frontend/js/components/queryHistoryHeader.js +48 -0
- package/frontend/js/components/queryHistoryList.js +132 -0
- package/frontend/js/components/queryHistoryPanel.js +72 -136
- package/frontend/js/components/structureGraph.js +699 -89
- package/frontend/js/components/tableDesignerEditor.js +3 -5
- package/frontend/js/store.js +73 -7
- package/frontend/js/utils/exportFilenames.js +3 -1
- package/frontend/js/views/charts.js +320 -169
- package/frontend/js/views/connections.js +59 -64
- package/frontend/js/views/data.js +12 -20
- package/frontend/js/views/editor.js +0 -2
- package/frontend/js/views/settings.js +78 -0
- package/frontend/js/views/structure.js +27 -13
- package/frontend/styles/components.css +155 -0
- package/frontend/styles/structure-graph.css +140 -35
- package/frontend/styles/tailwind.generated.css +1 -1
- package/frontend/styles/views.css +12 -6
- package/package.json +3 -2
- package/server/routes/export.js +89 -22
- package/server/routes/externalApi.js +84 -2
- package/server/routes/settings.js +37 -28
- package/server/services/appInfoService.js +215 -0
- package/server/services/databaseCommandService.js +50 -6
- package/server/services/sqlite/exportService.js +307 -22
- package/tests/api-token-auth.test.js +110 -1
- package/tests/cli-args.test.js +16 -3
- package/tests/database-command-service.test.js +39 -2
- package/tests/export-blob.test.js +54 -1
- package/tests/export-filenames.test.js +4 -0
- package/tests/settings-api-tokens-route.test.js +22 -1
- package/tests/settings-metadata.test.js +99 -1
- package/tests/settings-view.test.js +28 -0
|
@@ -1,39 +1,36 @@
|
|
|
1
|
-
import { renderConnectionCard } from
|
|
2
|
-
import { renderPageHeader } from
|
|
3
|
-
import { escapeHtml } from
|
|
1
|
+
import { renderConnectionCard } from '../components/connectionCard.js';
|
|
2
|
+
import { renderPageHeader } from '../components/pageHeader.js';
|
|
3
|
+
import { escapeHtml } from '../utils/format.js';
|
|
4
4
|
|
|
5
5
|
function renderConnectionsActionButton({
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
label,
|
|
7
|
+
icon,
|
|
8
|
+
action = 'open-modal',
|
|
9
|
+
modal,
|
|
10
|
+
tone = 'secondary',
|
|
11
|
+
disabled = false,
|
|
12
12
|
}) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
: "standard-button";
|
|
17
|
-
const iconClassName = "";
|
|
18
|
-
const modalAttribute = modal ? `data-modal="${modal}"` : "";
|
|
13
|
+
const toneClassName = tone === 'primary' ? 'signature-button' : 'standard-button';
|
|
14
|
+
const iconClassName = '';
|
|
15
|
+
const modalAttribute = modal ? `data-modal="${modal}"` : '';
|
|
19
16
|
|
|
20
|
-
|
|
17
|
+
return `
|
|
21
18
|
<button
|
|
22
19
|
class="${toneClassName}"
|
|
23
20
|
data-action="${escapeHtml(action)}"
|
|
24
21
|
${modalAttribute}
|
|
25
22
|
type="button"
|
|
26
|
-
${disabled ?
|
|
23
|
+
${disabled ? 'disabled' : ''}
|
|
27
24
|
>
|
|
28
25
|
<span>${label}</span>
|
|
29
|
-
<span class="material-symbols-outlined${iconClassName ? ` ${iconClassName}` :
|
|
26
|
+
<span class="material-symbols-outlined${iconClassName ? ` ${iconClassName}` : ''}">${icon}</span>
|
|
30
27
|
</button>
|
|
31
28
|
`;
|
|
32
29
|
}
|
|
33
30
|
|
|
34
31
|
function renderConnectionsBody(state) {
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
if (state.connections.loading && !state.connections.recent.length) {
|
|
33
|
+
return `
|
|
37
34
|
<div class="flex min-h-[280px] items-center justify-center border border-outline-variant/10 bg-surface-container-low">
|
|
38
35
|
<div class="text-center text-on-surface-variant/40">
|
|
39
36
|
<span class="material-symbols-outlined mb-3 text-4xl">progress_activity</span>
|
|
@@ -41,10 +38,10 @@ function renderConnectionsBody(state) {
|
|
|
41
38
|
</div>
|
|
42
39
|
</div>
|
|
43
40
|
`;
|
|
44
|
-
|
|
41
|
+
}
|
|
45
42
|
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
if (state.connections.error) {
|
|
44
|
+
return `
|
|
48
45
|
<div class="border border-error/20 bg-error-container/10 px-6 py-5 text-sm text-on-surface">
|
|
49
46
|
<div class="font-headline text-xs font-bold uppercase tracking-[0.18em] text-error">
|
|
50
47
|
${escapeHtml(state.connections.error.code)}
|
|
@@ -52,10 +49,10 @@ function renderConnectionsBody(state) {
|
|
|
52
49
|
<div class="mt-2">${escapeHtml(state.connections.error.message)}</div>
|
|
53
50
|
</div>
|
|
54
51
|
`;
|
|
55
|
-
|
|
52
|
+
}
|
|
56
53
|
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
if (!state.connections.recent.length) {
|
|
55
|
+
return `
|
|
59
56
|
<div class="border border-dashed border-outline-variant/20 bg-surface-container-low px-8 py-10 text-center">
|
|
60
57
|
<span class="material-symbols-outlined mb-3 text-5xl text-on-surface-variant/25">database_off</span>
|
|
61
58
|
<p class="font-headline text-xl font-black uppercase tracking-tight text-primary-container">
|
|
@@ -66,71 +63,69 @@ function renderConnectionsBody(state) {
|
|
|
66
63
|
</p>
|
|
67
64
|
<div class="mt-6 flex flex-wrap items-center justify-center gap-3">
|
|
68
65
|
${renderConnectionsActionButton({
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
tone: "primary",
|
|
66
|
+
label: 'Create Database',
|
|
67
|
+
icon: 'note_add',
|
|
68
|
+
modal: 'create-connection',
|
|
73
69
|
})}
|
|
74
70
|
${renderConnectionsActionButton({
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
label: 'Open Database',
|
|
72
|
+
icon: 'folder_open',
|
|
73
|
+
modal: 'open-connection',
|
|
74
|
+
tone: 'primary',
|
|
78
75
|
})}
|
|
79
76
|
</div>
|
|
80
77
|
</div>
|
|
81
78
|
`;
|
|
82
|
-
|
|
79
|
+
}
|
|
83
80
|
|
|
84
|
-
|
|
81
|
+
return `
|
|
85
82
|
<div class="grid grid-cols-1 gap-6 md:grid-cols-2 xl:grid-cols-3">
|
|
86
83
|
${state.connections.recent
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
)
|
|
90
|
-
.join("")}
|
|
84
|
+
.map(connection => renderConnectionCard(connection, state.connections.active?.id))
|
|
85
|
+
.join('')}
|
|
91
86
|
</div>
|
|
92
87
|
`;
|
|
93
88
|
}
|
|
94
89
|
|
|
95
90
|
export function renderConnectionsView(state) {
|
|
96
|
-
|
|
91
|
+
const actions = `
|
|
92
|
+
${
|
|
93
|
+
state.connections.active
|
|
94
|
+
? renderConnectionsActionButton({
|
|
95
|
+
label: state.connections.backupLoading ? 'Creating Backup...' : 'Create Backup',
|
|
96
|
+
icon: 'inventory_2',
|
|
97
|
+
action: 'create-backup',
|
|
98
|
+
disabled: state.connections.backupLoading,
|
|
99
|
+
})
|
|
100
|
+
: ''
|
|
101
|
+
}
|
|
97
102
|
${renderConnectionsActionButton({
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
tone: "primary",
|
|
103
|
+
label: 'Create Database',
|
|
104
|
+
icon: 'note_add',
|
|
105
|
+
modal: 'create-connection',
|
|
102
106
|
})}
|
|
103
107
|
${renderConnectionsActionButton({
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
108
|
+
label: 'Open Database',
|
|
109
|
+
icon: 'folder_open',
|
|
110
|
+
modal: 'open-connection',
|
|
111
|
+
tone: 'primary',
|
|
107
112
|
})}
|
|
108
|
-
${
|
|
109
|
-
state.connections.active
|
|
110
|
-
? renderConnectionsActionButton({
|
|
111
|
-
label: state.connections.backupLoading ? "Creating Backup..." : "Create Backup",
|
|
112
|
-
icon: "inventory_2",
|
|
113
|
-
action: "create-backup",
|
|
114
|
-
disabled: state.connections.backupLoading,
|
|
115
|
-
})
|
|
116
|
-
: ""
|
|
117
|
-
}
|
|
118
113
|
`;
|
|
119
114
|
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
return {
|
|
116
|
+
main: `
|
|
122
117
|
<section class="view-surface relative min-h-full overflow-hidden">
|
|
123
118
|
<div class="data-grid-texture pointer-events-none absolute inset-0"></div>
|
|
124
119
|
<div class="view-frame relative z-10">
|
|
125
120
|
${renderPageHeader({
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
121
|
+
title: 'Connections',
|
|
122
|
+
subtitle: `Registry // Recent_Targets: ${String(state.connections.recent.length).padStart(2, '0')}`,
|
|
123
|
+
actions,
|
|
129
124
|
})}
|
|
130
125
|
${renderConnectionsBody(state)}
|
|
131
126
|
</div>
|
|
132
127
|
</section>
|
|
133
128
|
`,
|
|
134
|
-
|
|
135
|
-
|
|
129
|
+
panel: '',
|
|
130
|
+
};
|
|
136
131
|
}
|
|
@@ -112,25 +112,8 @@ function renderWorkspaceHeader(state) {
|
|
|
112
112
|
const tablesVisible = state.dataBrowser.tablesVisible !== false;
|
|
113
113
|
|
|
114
114
|
return `
|
|
115
|
-
<header class="border-b border-outline-variant/10 bg-surface-container px-6 py-
|
|
116
|
-
<div class="flex flex-wrap items-
|
|
117
|
-
<div class="data-headline-container">
|
|
118
|
-
<div class="text-[10px] font-bold uppercase tracking-[0.2em] text-primary-container">
|
|
119
|
-
Data Browser
|
|
120
|
-
</div>
|
|
121
|
-
<h1 class="mt-2 font-headline text-4xl font-black uppercase tracking-tight text-primary-container">
|
|
122
|
-
${escapeHtml(table?.name ?? 'Table Data')}
|
|
123
|
-
</h1>
|
|
124
|
-
<div class="mt-2 text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
|
|
125
|
-
${
|
|
126
|
-
table
|
|
127
|
-
? `rows ${escapeHtml(formatNumber(table.rowCount ?? 0))} // columns ${escapeHtml(
|
|
128
|
-
formatNumber(table.columns?.length ?? 0),
|
|
129
|
-
)}`
|
|
130
|
-
: `tables ${escapeHtml(formatNumber(state.dataBrowser.tables.length))}`
|
|
131
|
-
}
|
|
132
|
-
</div>
|
|
133
|
-
</div>
|
|
115
|
+
<header class="border-b border-outline-variant/10 bg-surface-container px-6 py-3">
|
|
116
|
+
<div class="flex flex-wrap items-center justify-between gap-4">
|
|
134
117
|
<div class="flex items-center gap-3">
|
|
135
118
|
${
|
|
136
119
|
table
|
|
@@ -145,7 +128,14 @@ function renderWorkspaceHeader(state) {
|
|
|
145
128
|
: '<span class="material-symbols-outlined text-sm">visibility</span> Show Tables'
|
|
146
129
|
}
|
|
147
130
|
</button>
|
|
148
|
-
|
|
131
|
+
`
|
|
132
|
+
: ''
|
|
133
|
+
}
|
|
134
|
+
</div>
|
|
135
|
+
<div class="flex flex-wrap items-center justify-end gap-3">
|
|
136
|
+
${
|
|
137
|
+
table
|
|
138
|
+
? `<button
|
|
149
139
|
class="standard-button"
|
|
150
140
|
data-action="open-data-export-modal"
|
|
151
141
|
type="button"
|
|
@@ -476,6 +466,8 @@ function renderTableSurface(state) {
|
|
|
476
466
|
escapeHtml(formatNumber(totalRows)),
|
|
477
467
|
filteredRowsText,
|
|
478
468
|
' rows',
|
|
469
|
+
' // columns ',
|
|
470
|
+
escapeHtml(formatNumber(table.columns?.length ?? 0)),
|
|
479
471
|
'</div>',
|
|
480
472
|
'<div class="flex flex-wrap items-center gap-4"><div class="flex items-center gap-2">',
|
|
481
473
|
'<span class="text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">rows</span>',
|
|
@@ -307,8 +307,6 @@ export function renderEditorView(state, { isResultsRoute = false } = {}) {
|
|
|
307
307
|
query: state.editor.sqlText,
|
|
308
308
|
executing: state.editor.executing,
|
|
309
309
|
exporting: state.editor.exportLoading,
|
|
310
|
-
historyLoading: state.editor.historyLoading,
|
|
311
|
-
historyTotal: state.editor.historyTotal,
|
|
312
310
|
editorVisible: state.editor.editorPanelVisible,
|
|
313
311
|
historyVisible: state.editor.historyPanelVisible,
|
|
314
312
|
})}
|
|
@@ -30,6 +30,66 @@ function renderSettingsNavigation(activeSection) {
|
|
|
30
30
|
`;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
function renderVersionCheckStatus(settings) {
|
|
34
|
+
const result = settings.versionCheck;
|
|
35
|
+
const error = settings.versionCheckError;
|
|
36
|
+
|
|
37
|
+
if (error) {
|
|
38
|
+
return `
|
|
39
|
+
<div class="border border-error/20 bg-error-container/10 px-4 py-3 text-sm text-on-surface">
|
|
40
|
+
<div class="font-mono text-[10px] font-bold uppercase tracking-[0.18em] text-error">
|
|
41
|
+
${escapeHtml(error.code ?? 'VERSION_CHECK_FAILED')}
|
|
42
|
+
</div>
|
|
43
|
+
<div class="mt-1 text-on-surface-variant">${escapeHtml(error.message ?? 'Version check failed.')}</div>
|
|
44
|
+
</div>
|
|
45
|
+
`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!result) {
|
|
49
|
+
return `
|
|
50
|
+
<div class="border border-outline-variant/10 bg-surface-container-high px-4 py-3 text-sm text-on-surface-variant">
|
|
51
|
+
No version check has been run yet.
|
|
52
|
+
</div>
|
|
53
|
+
`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const updateAvailable = Boolean(result.updateAvailable);
|
|
57
|
+
const releaseLink =
|
|
58
|
+
updateAvailable && result.releaseUrl
|
|
59
|
+
? `
|
|
60
|
+
<a
|
|
61
|
+
class="standard-button"
|
|
62
|
+
href="${escapeHtml(result.releaseUrl)}"
|
|
63
|
+
rel="noreferrer"
|
|
64
|
+
target="_blank"
|
|
65
|
+
>
|
|
66
|
+
<span class="material-symbols-outlined text-sm">open_in_new</span>
|
|
67
|
+
Open Release
|
|
68
|
+
</a>
|
|
69
|
+
`
|
|
70
|
+
: '';
|
|
71
|
+
|
|
72
|
+
return `
|
|
73
|
+
<div class="border ${
|
|
74
|
+
updateAvailable ? 'border-primary-container/30 bg-primary-container/5' : 'border-outline-variant/10 bg-surface-container-high'
|
|
75
|
+
} px-4 py-3">
|
|
76
|
+
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
77
|
+
<div class="min-w-0">
|
|
78
|
+
<div class="font-mono text-[10px] font-bold uppercase tracking-[0.18em] ${
|
|
79
|
+
updateAvailable ? 'text-primary-container' : 'text-on-surface-variant/70'
|
|
80
|
+
}">
|
|
81
|
+
${updateAvailable ? 'New Version Available' : 'Up To Date'}
|
|
82
|
+
</div>
|
|
83
|
+
<div class="mt-1 text-sm text-on-surface-variant">
|
|
84
|
+
Current v${escapeHtml(result.currentVersion ?? '0.0.0')} · Latest v${escapeHtml(result.latestVersion ?? 'unknown')}
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
${releaseLink}
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
`;
|
|
91
|
+
}
|
|
92
|
+
|
|
33
93
|
function renderSettingsContent(state) {
|
|
34
94
|
if (state.settings.loading && !state.settings.appVersion) {
|
|
35
95
|
return `
|
|
@@ -56,6 +116,7 @@ function renderSettingsContent(state) {
|
|
|
56
116
|
const activeSection = state.settings.section === 'api-tokens' ? 'api-tokens' : 'information';
|
|
57
117
|
const appVersion = escapeHtml(state.settings.appVersion ?? '0.0.0');
|
|
58
118
|
const sqliteVersion = escapeHtml(state.settings.sqliteVersion ?? 'unknown');
|
|
119
|
+
const versionCheckLoading = Boolean(state.settings.versionCheckLoading);
|
|
59
120
|
const tokenDatabase = state.settings.tokenDatabase;
|
|
60
121
|
const apiTokens = state.settings.apiTokens ?? [];
|
|
61
122
|
const createdApiToken = state.settings.createdApiToken;
|
|
@@ -171,6 +232,23 @@ function renderSettingsContent(state) {
|
|
|
171
232
|
<div class="border-t border-outline-variant/10 py-4 text-sm leading-6 text-on-surface-variant">
|
|
172
233
|
SQL functions and syntax are evaluated with this SQLite runtime.
|
|
173
234
|
</div>
|
|
235
|
+
<div class="border-t border-outline-variant/10 pt-4">
|
|
236
|
+
<div class="mb-3 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
237
|
+
<div class="text-[10px] font-mono uppercase tracking-[0.2em] text-on-surface-variant/60">
|
|
238
|
+
Version_Check
|
|
239
|
+
</div>
|
|
240
|
+
<button
|
|
241
|
+
class="standard-button"
|
|
242
|
+
data-action="check-app-version"
|
|
243
|
+
type="button"
|
|
244
|
+
${versionCheckLoading ? 'disabled aria-disabled="true"' : ''}
|
|
245
|
+
>
|
|
246
|
+
<span class="material-symbols-outlined text-sm">${versionCheckLoading ? 'progress_activity' : 'sync'}</span>
|
|
247
|
+
${versionCheckLoading ? 'Checking...' : 'Check Updates'}
|
|
248
|
+
</button>
|
|
249
|
+
</div>
|
|
250
|
+
${renderVersionCheckStatus(state.settings)}
|
|
251
|
+
</div>
|
|
174
252
|
</div>
|
|
175
253
|
</section>
|
|
176
254
|
|
|
@@ -115,20 +115,27 @@ function renderObjectInspector(detail) {
|
|
|
115
115
|
return `
|
|
116
116
|
<div class="structure-graph__panel">
|
|
117
117
|
<div class="space-y-3">
|
|
118
|
-
<div class="structure-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
`
|
|
129
|
-
: ''
|
|
130
|
-
}
|
|
118
|
+
<div class="structure-graph__panel-heading">
|
|
119
|
+
<div class="structure-graph__eyebrow">Object Inspector</div>
|
|
120
|
+
<button
|
|
121
|
+
class="query-history-icon-button"
|
|
122
|
+
data-structure-graph-action="toggle-inspector"
|
|
123
|
+
type="button"
|
|
124
|
+
aria-label="Hide inspector"
|
|
125
|
+
>
|
|
126
|
+
<span class="material-symbols-outlined text-[18px]">close</span>
|
|
127
|
+
</button>
|
|
131
128
|
</div>
|
|
129
|
+
<div class="structure-graph__title">${escapeHtml(detail.name)}</div>
|
|
130
|
+
${
|
|
131
|
+
detail.tableName
|
|
132
|
+
? `
|
|
133
|
+
<div class="structure-graph__subtitle">
|
|
134
|
+
TABLE ${escapeHtml(detail.tableName)}
|
|
135
|
+
</div>
|
|
136
|
+
`
|
|
137
|
+
: ''
|
|
138
|
+
}
|
|
132
139
|
</div>
|
|
133
140
|
|
|
134
141
|
<div class="structure-graph__summary">
|
|
@@ -255,6 +262,8 @@ function renderGraphSurface(structure, selectedName, detail, detailLoading, tabl
|
|
|
255
262
|
<span class="material-symbols-outlined text-sm">table_rows</span>
|
|
256
263
|
Open Data
|
|
257
264
|
</button>
|
|
265
|
+
</div>
|
|
266
|
+
<div class="structure-graph__toolbar-inspector">
|
|
258
267
|
<button
|
|
259
268
|
class="${toolbarButtonClass}"
|
|
260
269
|
data-structure-graph-action="toggle-inspector"
|
|
@@ -280,6 +289,11 @@ function renderGraphSurface(structure, selectedName, detail, detailLoading, tabl
|
|
|
280
289
|
The active database does not expose table metadata for the schema graph.
|
|
281
290
|
</p>
|
|
282
291
|
</div>
|
|
292
|
+
<div
|
|
293
|
+
class="structure-graph__edge-readout"
|
|
294
|
+
data-structure-graph-edge-readout
|
|
295
|
+
hidden
|
|
296
|
+
></div>
|
|
283
297
|
</div>
|
|
284
298
|
|
|
285
299
|
<aside
|
|
@@ -624,13 +624,35 @@
|
|
|
624
624
|
}
|
|
625
625
|
|
|
626
626
|
.status-badge {
|
|
627
|
+
align-items: center;
|
|
627
628
|
border: 1px solid transparent;
|
|
629
|
+
box-sizing: border-box;
|
|
628
630
|
display: inline-flex;
|
|
629
631
|
font-size: var(--font-size-micro);
|
|
630
632
|
font-weight: 700;
|
|
633
|
+
justify-content: center;
|
|
631
634
|
letter-spacing: 0.12em;
|
|
635
|
+
line-height: 1;
|
|
632
636
|
padding: 0.125rem 0.5rem;
|
|
633
637
|
text-transform: uppercase;
|
|
638
|
+
white-space: nowrap;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
.query-history-badge-row {
|
|
642
|
+
align-items: center;
|
|
643
|
+
display: flex;
|
|
644
|
+
flex-wrap: wrap;
|
|
645
|
+
gap: var(--spacing-2);
|
|
646
|
+
width: 100%;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
.query-history-badge-row .status-badge {
|
|
650
|
+
min-height: var(--button-height);
|
|
651
|
+
min-width: 8.5rem;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
.query-history-badge-row--compact .status-badge {
|
|
655
|
+
min-width: 6.75rem;
|
|
634
656
|
}
|
|
635
657
|
|
|
636
658
|
.status-badge--primary {
|
|
@@ -746,6 +768,76 @@
|
|
|
746
768
|
width: clamp(20rem, 26vw, 22.5rem);
|
|
747
769
|
}
|
|
748
770
|
|
|
771
|
+
.query-history-panel__header {
|
|
772
|
+
border-bottom: 1px solid var(--border-medium);
|
|
773
|
+
padding: var(--spacing-4);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
.query-history-tabs {
|
|
777
|
+
align-items: center;
|
|
778
|
+
display: flex;
|
|
779
|
+
gap: var(--spacing-2);
|
|
780
|
+
margin-top: var(--spacing-4);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
.query-history-tabs__count {
|
|
784
|
+
color: var(--text-variant-subtle);
|
|
785
|
+
font-family: var(--font-family-mono);
|
|
786
|
+
font-size: 10px;
|
|
787
|
+
letter-spacing: 0.16em;
|
|
788
|
+
margin-left: auto;
|
|
789
|
+
text-transform: uppercase;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
.query-history-list-scroll {
|
|
793
|
+
flex: 1;
|
|
794
|
+
min-height: 0;
|
|
795
|
+
overflow: auto;
|
|
796
|
+
padding: var(--spacing-3);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
.query-history-list {
|
|
800
|
+
display: flex;
|
|
801
|
+
flex-direction: column;
|
|
802
|
+
gap: var(--spacing-3);
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
.query-editor-toolbar {
|
|
806
|
+
align-items: center;
|
|
807
|
+
display: grid;
|
|
808
|
+
gap: var(--spacing-4);
|
|
809
|
+
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
.query-editor-toolbar__left,
|
|
813
|
+
.query-editor-toolbar__center,
|
|
814
|
+
.query-editor-toolbar__right {
|
|
815
|
+
align-items: center;
|
|
816
|
+
display: flex;
|
|
817
|
+
gap: var(--spacing-3);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
.query-editor-toolbar__center {
|
|
821
|
+
flex-wrap: wrap;
|
|
822
|
+
justify-content: center;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
.query-editor-toolbar__right {
|
|
826
|
+
justify-content: flex-end;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
@media (max-width: 980px) {
|
|
830
|
+
.query-editor-toolbar {
|
|
831
|
+
grid-template-columns: 1fr;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
.query-editor-toolbar__left,
|
|
835
|
+
.query-editor-toolbar__center,
|
|
836
|
+
.query-editor-toolbar__right {
|
|
837
|
+
justify-content: flex-start;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
749
841
|
.table-designer-sidebar {
|
|
750
842
|
background: var(--color-surface-low);
|
|
751
843
|
border-right: 1px solid var(--border-medium);
|
|
@@ -1478,6 +1570,63 @@ input.table-designer-main__name[type='text'] {
|
|
|
1478
1570
|
background: var(--primary-alpha-08);
|
|
1479
1571
|
}
|
|
1480
1572
|
|
|
1573
|
+
.query-history-item-main {
|
|
1574
|
+
display: flex;
|
|
1575
|
+
flex-direction: column;
|
|
1576
|
+
gap: var(--spacing-2);
|
|
1577
|
+
text-align: left;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
.query-history-item-title {
|
|
1581
|
+
color: var(--color-on-surface);
|
|
1582
|
+
display: block;
|
|
1583
|
+
font-family: var(--font-family-headline);
|
|
1584
|
+
font-size: 0.875rem;
|
|
1585
|
+
font-weight: 700;
|
|
1586
|
+
letter-spacing: 0;
|
|
1587
|
+
line-height: 1.15;
|
|
1588
|
+
min-width: 0;
|
|
1589
|
+
overflow: hidden;
|
|
1590
|
+
text-overflow: ellipsis;
|
|
1591
|
+
text-transform: uppercase;
|
|
1592
|
+
white-space: nowrap;
|
|
1593
|
+
width: 100%;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
.query-history-item.is-active .query-history-item-title,
|
|
1597
|
+
.query-history-item-hit.is-active .query-history-item-title {
|
|
1598
|
+
color: var(--color-primary-container);
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
.query-history-item-footer {
|
|
1602
|
+
align-items: center;
|
|
1603
|
+
border-top: 1px solid var(--border-subtle);
|
|
1604
|
+
display: flex;
|
|
1605
|
+
gap: var(--spacing-3);
|
|
1606
|
+
justify-content: space-between;
|
|
1607
|
+
padding: var(--spacing-2) var(--spacing-3) var(--spacing-3);
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
.query-history-item-meta {
|
|
1611
|
+
color: var(--text-variant-subtle);
|
|
1612
|
+
display: flex;
|
|
1613
|
+
flex-wrap: wrap;
|
|
1614
|
+
font-family: var(--font-family-mono);
|
|
1615
|
+
font-size: 10px;
|
|
1616
|
+
gap: var(--spacing-2);
|
|
1617
|
+
letter-spacing: 0.14em;
|
|
1618
|
+
min-width: 0;
|
|
1619
|
+
text-transform: uppercase;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
.query-history-item-actions {
|
|
1623
|
+
align-items: center;
|
|
1624
|
+
display: flex;
|
|
1625
|
+
flex-shrink: 0;
|
|
1626
|
+
gap: var(--spacing-1);
|
|
1627
|
+
margin-left: auto;
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1481
1630
|
.query-history-icon-button {
|
|
1482
1631
|
background: transparent;
|
|
1483
1632
|
border: 0;
|
|
@@ -1522,10 +1671,16 @@ input.table-designer-main__name[type='text'] {
|
|
|
1522
1671
|
}
|
|
1523
1672
|
|
|
1524
1673
|
.query-history-sql-preview {
|
|
1674
|
+
color: rgb(var(--rgb-on-surface-variant) / 0.75);
|
|
1525
1675
|
display: -webkit-box;
|
|
1526
1676
|
-webkit-box-orient: vertical;
|
|
1527
1677
|
-webkit-line-clamp: 2;
|
|
1678
|
+
font-family: var(--font-family-mono);
|
|
1679
|
+
font-size: 0.75rem;
|
|
1680
|
+
line-height: 1.25rem;
|
|
1681
|
+
margin-top: var(--spacing-2);
|
|
1528
1682
|
overflow: hidden;
|
|
1683
|
+
text-align: left;
|
|
1529
1684
|
}
|
|
1530
1685
|
|
|
1531
1686
|
.query-history-detail-sql {
|