sqlite-hub 1.4.0 → 2.0.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 +14 -1
- package/bin/sqlite-hub-mcp.js +8 -0
- package/bin/sqlite-hub.js +555 -122
- package/docs/API.md +30 -0
- package/docs/CLI.md +22 -0
- package/docs/CLI_API_PARITY.md +61 -0
- package/docs/MCP.md +85 -0
- package/docs/changelog.md +13 -1
- package/docs/guidelines/AGENTS.md +32 -0
- package/docs/{DESIGN_GUIDELINES.md → guidelines/DESIGN.md} +10 -0
- package/docs/todo.md +1 -14
- package/frontend/js/api.js +49 -0
- package/frontend/js/app.js +202 -2
- package/frontend/js/components/connectionCard.js +3 -1
- package/frontend/js/components/modal.js +356 -15
- package/frontend/js/components/sidebar.js +41 -7
- package/frontend/js/components/topNav.js +2 -14
- package/frontend/js/router.js +10 -0
- package/frontend/js/store.js +483 -7
- package/frontend/js/utils/inputClear.js +36 -0
- package/frontend/js/utils/syntheticData.js +240 -0
- package/frontend/js/views/backups.js +52 -0
- package/frontend/js/views/data.js +16 -0
- package/frontend/js/views/logs.js +339 -0
- package/frontend/js/views/settings.js +266 -30
- package/frontend/js/views/structure.js +6 -0
- package/frontend/js/views/tableAdvisor.js +385 -0
- package/frontend/js/views/tableDesigner.js +6 -0
- package/frontend/styles/components.css +149 -0
- package/frontend/styles/tailwind.generated.css +1 -1
- package/frontend/styles/views.css +31 -0
- package/package.json +4 -2
- package/server/mcp/stdioServer.js +272 -0
- package/server/routes/data.js +45 -0
- package/server/routes/externalApi.js +285 -2
- package/server/routes/logs.js +127 -0
- package/server/routes/settings.js +47 -0
- package/server/server.js +3 -0
- package/server/services/databaseCommandService.js +284 -2
- package/server/services/mcpStatusService.js +140 -0
- package/server/services/mcpToolService.js +274 -0
- package/server/services/sqlite/dataBrowserService.js +36 -0
- package/server/services/sqlite/introspection.js +226 -22
- package/server/services/sqlite/syntheticDataGenerator.js +728 -0
- package/server/services/sqlite/tableAdvisor.js +790 -0
- package/server/services/storage/appStateStore.js +821 -169
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import {
|
|
2
|
+
escapeHtml,
|
|
3
|
+
formatDateTime,
|
|
4
|
+
formatNumber,
|
|
5
|
+
highlightSql,
|
|
6
|
+
truncateMiddle,
|
|
7
|
+
} from '../utils/format.js';
|
|
8
|
+
import { renderWorkspaceOpenDropdown } from '../components/workspaceOpenDropdown.js';
|
|
9
|
+
|
|
10
|
+
const CATEGORY_LABELS = {
|
|
11
|
+
schema: 'Schema',
|
|
12
|
+
constraints: 'Constraints',
|
|
13
|
+
performance: 'Performance',
|
|
14
|
+
'data-quality': 'Data Quality',
|
|
15
|
+
documentation: 'Documentation',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const CATEGORY_ORDER = ['schema', 'constraints', 'performance', 'data-quality', 'documentation'];
|
|
19
|
+
|
|
20
|
+
function getScoreClass(score) {
|
|
21
|
+
if (score >= 80) {
|
|
22
|
+
return 'text-primary-container';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (score >= 60) {
|
|
26
|
+
return 'text-on-surface';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return 'text-error';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function renderError(error) {
|
|
33
|
+
if (!error) {
|
|
34
|
+
return '';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return `
|
|
38
|
+
<div class="border border-error/20 bg-error-container/10 px-6 py-5 text-sm text-on-surface">
|
|
39
|
+
<div class="font-body text-xs font-bold uppercase tracking-[0.18em] text-error">
|
|
40
|
+
${escapeHtml(error.code ?? 'REQUEST_FAILED')}
|
|
41
|
+
</div>
|
|
42
|
+
<div class="mt-2">${escapeHtml(error.message ?? 'Request failed.')}</div>
|
|
43
|
+
</div>
|
|
44
|
+
`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function renderSeverityBadge(severity = 'info') {
|
|
48
|
+
const normalized = String(severity ?? 'info').toLowerCase();
|
|
49
|
+
const className =
|
|
50
|
+
normalized === 'critical'
|
|
51
|
+
? 'border-error/35 bg-error-container/25 text-error'
|
|
52
|
+
: normalized === 'warning'
|
|
53
|
+
? 'border-primary-container/35 bg-primary-container/15 text-primary-container'
|
|
54
|
+
: 'border-outline-variant/20 bg-surface-container-lowest text-on-surface-variant';
|
|
55
|
+
|
|
56
|
+
return `<span class="inline-flex h-6 items-center border px-2 font-mono text-[10px] font-bold uppercase tracking-[0.16em] ${className}">${escapeHtml(
|
|
57
|
+
normalized,
|
|
58
|
+
)}</span>`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function renderRiskBadge(risk = 'low') {
|
|
62
|
+
const normalized = String(risk ?? 'low').toLowerCase();
|
|
63
|
+
const className =
|
|
64
|
+
normalized === 'high'
|
|
65
|
+
? 'border-error/35 text-error'
|
|
66
|
+
: normalized === 'medium'
|
|
67
|
+
? 'border-primary-container/35 text-primary-container'
|
|
68
|
+
: 'border-outline-variant/20 text-on-surface-variant/70';
|
|
69
|
+
|
|
70
|
+
return `<span class="inline-flex h-6 items-center border bg-surface-container-lowest px-2 font-mono text-[10px] font-bold uppercase tracking-[0.16em] ${className}">Risk ${escapeHtml(
|
|
71
|
+
normalized,
|
|
72
|
+
)}</span>`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function renderTableList(state) {
|
|
76
|
+
const { tables = [], selectedTableName } = state.tableAdvisor;
|
|
77
|
+
|
|
78
|
+
if (state.tableAdvisor.loading && !tables.length) {
|
|
79
|
+
return `
|
|
80
|
+
<div class="px-4 py-6 text-sm text-on-surface-variant/45">
|
|
81
|
+
Loading tables...
|
|
82
|
+
</div>
|
|
83
|
+
`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!tables.length) {
|
|
87
|
+
return `
|
|
88
|
+
<div class="px-4 py-6 text-sm text-on-surface-variant/45">
|
|
89
|
+
No tables found.
|
|
90
|
+
</div>
|
|
91
|
+
`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return `
|
|
95
|
+
<div class="subnavi-list custom-scrollbar">
|
|
96
|
+
${tables
|
|
97
|
+
.map(table => {
|
|
98
|
+
const isActive = table.name === selectedTableName;
|
|
99
|
+
|
|
100
|
+
return `
|
|
101
|
+
<a
|
|
102
|
+
class="table-designer-sidebar__item subnavi-item block border px-4 py-3 text-left transition-colors ${
|
|
103
|
+
isActive
|
|
104
|
+
? 'is-active border-primary-container/30 bg-surface-container-high'
|
|
105
|
+
: 'border-outline-variant/10 bg-surface-container-lowest hover:bg-surface-container-high'
|
|
106
|
+
}"
|
|
107
|
+
href="#/table-advisor/${encodeURIComponent(table.name)}"
|
|
108
|
+
title="${escapeHtml(table.name)}"
|
|
109
|
+
>
|
|
110
|
+
<div class="table-designer-sidebar__item-name ${isActive ? 'is-active' : ''}">
|
|
111
|
+
${escapeHtml(table.name)}
|
|
112
|
+
</div>
|
|
113
|
+
<div class="table-designer-sidebar__item-meta">
|
|
114
|
+
${escapeHtml(formatNumber(table.columnCount ?? 0))} columns
|
|
115
|
+
</div>
|
|
116
|
+
</a>
|
|
117
|
+
`;
|
|
118
|
+
})
|
|
119
|
+
.join('')}
|
|
120
|
+
</div>
|
|
121
|
+
`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function renderSummary(result = {}) {
|
|
125
|
+
const score = Number(result.score ?? 0);
|
|
126
|
+
|
|
127
|
+
return `
|
|
128
|
+
<div class="grid grid-cols-1 gap-3 md:grid-cols-4">
|
|
129
|
+
<div class="border border-outline-variant/10 bg-surface-container-low px-4 py-3">
|
|
130
|
+
<div class="font-mono text-[10px] uppercase tracking-[0.2em] text-on-surface-variant/55">Table</div>
|
|
131
|
+
<div class="mt-2 min-w-0 truncate text-lg font-black text-on-surface" title="${escapeHtml(
|
|
132
|
+
result.tableName ?? '',
|
|
133
|
+
)}">${escapeHtml(result.tableName ?? 'Unknown')}</div>
|
|
134
|
+
</div>
|
|
135
|
+
<div class="border border-outline-variant/10 bg-surface-container-low px-4 py-3">
|
|
136
|
+
<div class="font-mono text-[10px] uppercase tracking-[0.2em] text-on-surface-variant/55">Score</div>
|
|
137
|
+
<div class="mt-2 text-3xl font-black ${getScoreClass(score)}">${escapeHtml(formatNumber(score))}</div>
|
|
138
|
+
</div>
|
|
139
|
+
<div class="border border-outline-variant/10 bg-surface-container-low px-4 py-3">
|
|
140
|
+
<div class="font-mono text-[10px] uppercase tracking-[0.2em] text-on-surface-variant/55">Issues</div>
|
|
141
|
+
<div class="mt-2 text-3xl font-black text-on-surface">${escapeHtml(formatNumber(result.issueCount ?? 0))}</div>
|
|
142
|
+
</div>
|
|
143
|
+
<div class="border border-outline-variant/10 bg-surface-container-low px-4 py-3">
|
|
144
|
+
<div class="font-mono text-[10px] uppercase tracking-[0.2em] text-on-surface-variant/55">Rows</div>
|
|
145
|
+
<div class="mt-2 text-3xl font-black text-on-surface">${escapeHtml(formatNumber(result.rowCount ?? 0))}</div>
|
|
146
|
+
<div class="mt-1 truncate font-mono text-[10px] uppercase tracking-[0.14em] text-on-surface-variant/45">
|
|
147
|
+
${escapeHtml(formatDateTime(result.analyzedAt))}
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function renderIssue(issue = {}) {
|
|
155
|
+
const sql = String(issue.sql ?? '').trim();
|
|
156
|
+
|
|
157
|
+
return `
|
|
158
|
+
<article class="border border-outline-variant/10 bg-surface-container-lowest px-4 py-4">
|
|
159
|
+
<div class="flex flex-wrap items-start justify-between gap-3">
|
|
160
|
+
<div class="min-w-0">
|
|
161
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
162
|
+
${renderSeverityBadge(issue.severity)}
|
|
163
|
+
${renderRiskBadge(issue.risk)}
|
|
164
|
+
</div>
|
|
165
|
+
<h4 class="mt-3 text-lg font-black text-on-surface">${escapeHtml(issue.title ?? 'Advisor issue')}</h4>
|
|
166
|
+
</div>
|
|
167
|
+
<div class="font-mono text-[10px] uppercase tracking-[0.16em] text-on-surface-variant/45" title="${escapeHtml(
|
|
168
|
+
issue.id ?? '',
|
|
169
|
+
)}">
|
|
170
|
+
${escapeHtml(truncateMiddle(issue.id ?? '', 42))}
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
<div class="mt-3 grid grid-cols-1 gap-3 text-sm leading-6 text-on-surface-variant/75 lg:grid-cols-3">
|
|
174
|
+
<div>
|
|
175
|
+
<div class="font-mono text-[10px] uppercase tracking-[0.18em] text-on-surface-variant/45">Explanation</div>
|
|
176
|
+
<p class="mt-1">${escapeHtml(issue.explanation ?? '')}</p>
|
|
177
|
+
</div>
|
|
178
|
+
<div>
|
|
179
|
+
<div class="font-mono text-[10px] uppercase tracking-[0.18em] text-on-surface-variant/45">Evidence</div>
|
|
180
|
+
<p class="mt-1">${escapeHtml(issue.evidence ?? '')}</p>
|
|
181
|
+
</div>
|
|
182
|
+
<div>
|
|
183
|
+
<div class="font-mono text-[10px] uppercase tracking-[0.18em] text-on-surface-variant/45">Recommendation</div>
|
|
184
|
+
<p class="mt-1">${escapeHtml(issue.recommendation ?? '')}</p>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
${
|
|
188
|
+
issue.risk === 'high'
|
|
189
|
+
? `<div class="mt-3 border border-error/20 bg-error-container/15 px-3 py-2 text-xs leading-5 text-error">
|
|
190
|
+
This change may require rebuilding the table. Create a backup before applying it.
|
|
191
|
+
</div>`
|
|
192
|
+
: ''
|
|
193
|
+
}
|
|
194
|
+
${
|
|
195
|
+
sql
|
|
196
|
+
? `<div class="mt-4 border border-outline-variant/10 bg-surface-container-low">
|
|
197
|
+
<div class="flex items-center justify-between gap-3 border-b border-outline-variant/10 px-3 py-2">
|
|
198
|
+
<div class="font-mono text-[10px] uppercase tracking-[0.18em] text-primary-container">SQL</div>
|
|
199
|
+
<button
|
|
200
|
+
class="standard-button min-h-8 px-3 py-1 text-[10px]"
|
|
201
|
+
data-action="copy-table-advisor-sql"
|
|
202
|
+
data-issue-id="${escapeHtml(issue.id ?? '')}"
|
|
203
|
+
type="button"
|
|
204
|
+
>
|
|
205
|
+
<span class="material-symbols-outlined text-sm">content_copy</span>
|
|
206
|
+
Copy SQL
|
|
207
|
+
</button>
|
|
208
|
+
</div>
|
|
209
|
+
<pre class="custom-scrollbar max-h-48 overflow-auto whitespace-pre-wrap px-3 py-3 text-xs leading-6 text-on-surface"><code>${highlightSql(
|
|
210
|
+
sql,
|
|
211
|
+
)}</code></pre>
|
|
212
|
+
</div>`
|
|
213
|
+
: ''
|
|
214
|
+
}
|
|
215
|
+
</article>
|
|
216
|
+
`;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function renderIssues(result = {}) {
|
|
220
|
+
const issues = result.issues ?? [];
|
|
221
|
+
|
|
222
|
+
if (!issues.length) {
|
|
223
|
+
return `
|
|
224
|
+
<div class="border border-outline-variant/10 bg-surface-container-lowest px-4 py-6 text-sm leading-6 text-on-surface-variant/70">
|
|
225
|
+
No table advisor issues found for this table.
|
|
226
|
+
</div>
|
|
227
|
+
`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return CATEGORY_ORDER.map(category => {
|
|
231
|
+
const categoryIssues = issues.filter(issue => issue.category === category);
|
|
232
|
+
|
|
233
|
+
if (!categoryIssues.length) {
|
|
234
|
+
return '';
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return `
|
|
238
|
+
<section class="space-y-3">
|
|
239
|
+
<div class="flex items-center justify-between gap-3 border-b border-outline-variant/10 pb-2">
|
|
240
|
+
<h3 class="font-mono text-[11px] uppercase tracking-[0.22em] text-primary-container">
|
|
241
|
+
${escapeHtml(CATEGORY_LABELS[category] ?? category)}
|
|
242
|
+
</h3>
|
|
243
|
+
<div class="font-mono text-[10px] uppercase tracking-[0.16em] text-on-surface-variant/45">
|
|
244
|
+
${escapeHtml(formatNumber(categoryIssues.length))}
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
${categoryIssues.map(issue => renderIssue(issue)).join('')}
|
|
248
|
+
</section>
|
|
249
|
+
`;
|
|
250
|
+
}).join('');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function renderAdvisorBody(state) {
|
|
254
|
+
const advisor = state.tableAdvisor;
|
|
255
|
+
|
|
256
|
+
if (advisor.error) {
|
|
257
|
+
return renderError(advisor.error);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (advisor.loading && !advisor.result) {
|
|
261
|
+
return `
|
|
262
|
+
<div class="flex min-h-[280px] items-center justify-center border border-outline-variant/10 bg-surface-container-low">
|
|
263
|
+
<div class="text-center text-on-surface-variant/40">
|
|
264
|
+
<span class="material-symbols-outlined mb-3 text-4xl">progress_activity</span>
|
|
265
|
+
<p class="font-mono text-[10px] uppercase tracking-[0.22em]">LOADING_TABLES</p>
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
`;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (!advisor.selectedTableName) {
|
|
272
|
+
return `
|
|
273
|
+
<div class="border border-dashed border-outline-variant/20 bg-surface-container-low px-8 py-10 text-center">
|
|
274
|
+
<span class="material-symbols-outlined mb-3 text-5xl text-on-surface-variant/25">troubleshoot</span>
|
|
275
|
+
<p class="font-body text-xl font-black uppercase tracking-tight text-primary-container">
|
|
276
|
+
No table selected
|
|
277
|
+
</p>
|
|
278
|
+
</div>
|
|
279
|
+
`;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (advisor.analysisLoading) {
|
|
283
|
+
return `
|
|
284
|
+
<div class="flex min-h-[280px] items-center justify-center border border-outline-variant/10 bg-surface-container-low">
|
|
285
|
+
<div class="text-center text-on-surface-variant/40">
|
|
286
|
+
<span class="material-symbols-outlined mb-3 text-4xl">progress_activity</span>
|
|
287
|
+
<p class="font-mono text-[10px] uppercase tracking-[0.22em]">ANALYZING_TABLE</p>
|
|
288
|
+
</div>
|
|
289
|
+
</div>
|
|
290
|
+
`;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (advisor.analysisError) {
|
|
294
|
+
return renderError(advisor.analysisError);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (!advisor.result) {
|
|
298
|
+
return '';
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return `
|
|
302
|
+
<div class="space-y-6">
|
|
303
|
+
${renderSummary(advisor.result)}
|
|
304
|
+
${renderIssues(advisor.result)}
|
|
305
|
+
</div>
|
|
306
|
+
`;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function renderToolbar(state) {
|
|
310
|
+
const tableName = state.tableAdvisor.selectedTableName;
|
|
311
|
+
|
|
312
|
+
return `
|
|
313
|
+
<header class="workspace-header">
|
|
314
|
+
<div class="min-w-0">
|
|
315
|
+
<div class="font-mono text-[10px] uppercase tracking-[0.26em] text-primary-container/70">
|
|
316
|
+
Local analysis
|
|
317
|
+
</div>
|
|
318
|
+
<h1 class="mt-1 truncate font-body text-3xl font-black uppercase tracking-tight text-primary-container">
|
|
319
|
+
Table Advisor
|
|
320
|
+
</h1>
|
|
321
|
+
</div>
|
|
322
|
+
<div class="ml-auto flex flex-wrap items-center justify-end gap-3">
|
|
323
|
+
${renderWorkspaceOpenDropdown({
|
|
324
|
+
tableName: tableName ?? '',
|
|
325
|
+
destinations: [
|
|
326
|
+
{
|
|
327
|
+
icon: 'table_rows',
|
|
328
|
+
key: 'data',
|
|
329
|
+
label: 'Data',
|
|
330
|
+
target: name => `/data/${encodeURIComponent(name)}`,
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
icon: 'account_tree',
|
|
334
|
+
key: 'structure',
|
|
335
|
+
label: 'Structure',
|
|
336
|
+
target: name => `/structure/${encodeURIComponent(name)}`,
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
icon: 'table_chart',
|
|
340
|
+
key: 'table-designer',
|
|
341
|
+
label: 'Table Designer',
|
|
342
|
+
target: name => `/table-designer/${encodeURIComponent(name)}`,
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
key: 'sql-editor',
|
|
346
|
+
},
|
|
347
|
+
],
|
|
348
|
+
})}
|
|
349
|
+
<button class="standard-button" data-action="refresh-view" type="button">
|
|
350
|
+
<span class="material-symbols-outlined text-sm">refresh</span>
|
|
351
|
+
Refresh
|
|
352
|
+
</button>
|
|
353
|
+
</div>
|
|
354
|
+
</header>
|
|
355
|
+
`;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export function renderTableAdvisorView(state) {
|
|
359
|
+
return {
|
|
360
|
+
main: `
|
|
361
|
+
<section class="view-surface flex h-full min-h-full min-h-0 flex-col overflow-hidden bg-surface-container">
|
|
362
|
+
<div class="data-view-grid data-view-grid--with-subnavi">
|
|
363
|
+
<aside class="subnavi-panel border-r border-outline-variant/10 bg-surface-low">
|
|
364
|
+
<div class="subnavi-header">
|
|
365
|
+
<div>
|
|
366
|
+
<div class="subnavi-header-title">Tables</div>
|
|
367
|
+
<div class="subnavi-header-details">
|
|
368
|
+
total ${escapeHtml(formatNumber(state.tableAdvisor.tables.length))}
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
</div>
|
|
372
|
+
${renderTableList(state)}
|
|
373
|
+
</aside>
|
|
374
|
+
<section class="flex min-h-0 flex-col overflow-hidden">
|
|
375
|
+
${renderToolbar(state)}
|
|
376
|
+
<div class="custom-scrollbar min-h-0 flex-1 overflow-auto p-6">
|
|
377
|
+
${renderAdvisorBody(state)}
|
|
378
|
+
</div>
|
|
379
|
+
</section>
|
|
380
|
+
</div>
|
|
381
|
+
</section>
|
|
382
|
+
`,
|
|
383
|
+
panel: '',
|
|
384
|
+
};
|
|
385
|
+
}
|
|
@@ -51,6 +51,12 @@ function renderWorkspaceToolbar(state) {
|
|
|
51
51
|
label: "Structure",
|
|
52
52
|
target: name => `/structure/${encodeURIComponent(name)}`,
|
|
53
53
|
},
|
|
54
|
+
{
|
|
55
|
+
icon: "troubleshoot",
|
|
56
|
+
key: "table-advisor",
|
|
57
|
+
label: "Table Advisor",
|
|
58
|
+
target: name => `/table-advisor/${encodeURIComponent(name)}`,
|
|
59
|
+
},
|
|
54
60
|
{
|
|
55
61
|
key: "sql-editor",
|
|
56
62
|
},
|
|
@@ -452,6 +452,10 @@
|
|
|
452
452
|
overflow-y: auto;
|
|
453
453
|
}
|
|
454
454
|
|
|
455
|
+
.app-modal-body--no-scroll {
|
|
456
|
+
overflow: hidden;
|
|
457
|
+
}
|
|
458
|
+
|
|
455
459
|
.type-generation-code-preview {
|
|
456
460
|
display: block;
|
|
457
461
|
height: clamp(20rem, 50vh, 32rem);
|
|
@@ -460,6 +464,151 @@
|
|
|
460
464
|
scrollbar-gutter: stable;
|
|
461
465
|
}
|
|
462
466
|
|
|
467
|
+
.generate-data-modal-form {
|
|
468
|
+
display: flex;
|
|
469
|
+
flex-direction: column;
|
|
470
|
+
gap: var(--spacing-4);
|
|
471
|
+
min-height: 0;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.synthetic-generator-count-row {
|
|
475
|
+
display: grid;
|
|
476
|
+
gap: var(--spacing-4);
|
|
477
|
+
grid-template-columns: 12rem minmax(0, 1fr);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.synthetic-generator-grid {
|
|
481
|
+
border: 1px solid var(--border-subtle);
|
|
482
|
+
max-height: min(44vh, 34rem);
|
|
483
|
+
max-height: min(44dvh, 34rem);
|
|
484
|
+
overflow: auto;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
.synthetic-generator-grid__header,
|
|
488
|
+
.synthetic-generator-grid__row {
|
|
489
|
+
display: grid;
|
|
490
|
+
gap: var(--spacing-4);
|
|
491
|
+
grid-template-columns: minmax(12rem, 0.95fr) minmax(9rem, 0.6fr) minmax(13rem, 0.75fr) minmax(18rem, 1.2fr);
|
|
492
|
+
min-width: 58rem;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.synthetic-generator-grid__header {
|
|
496
|
+
background: var(--color-surface-container-low);
|
|
497
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
498
|
+
color: rgb(var(--rgb-on-surface-variant) / 0.65);
|
|
499
|
+
font-family: var(--font-family-mono);
|
|
500
|
+
font-size: 10px;
|
|
501
|
+
font-weight: 700;
|
|
502
|
+
letter-spacing: 0.18em;
|
|
503
|
+
padding: var(--spacing-4);
|
|
504
|
+
text-transform: uppercase;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.synthetic-generator-grid__row {
|
|
508
|
+
align-items: start;
|
|
509
|
+
background: var(--color-surface-container-lowest);
|
|
510
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
511
|
+
padding: var(--spacing-4);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.synthetic-generator-grid__row:last-child {
|
|
515
|
+
border-bottom: 0;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.synthetic-generator-column-name {
|
|
519
|
+
color: var(--color-on-surface);
|
|
520
|
+
font-family: var(--font-family-body);
|
|
521
|
+
font-size: 13px;
|
|
522
|
+
font-weight: 900;
|
|
523
|
+
overflow-wrap: anywhere;
|
|
524
|
+
text-transform: uppercase;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
.synthetic-generator-column-note {
|
|
528
|
+
color: rgb(var(--rgb-on-surface-variant) / 0.55);
|
|
529
|
+
font-size: 12px;
|
|
530
|
+
margin-top: var(--spacing-1);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.synthetic-generator-sqlite-type {
|
|
534
|
+
color: rgb(var(--rgb-on-surface-variant) / 0.78);
|
|
535
|
+
display: flex;
|
|
536
|
+
flex-direction: column;
|
|
537
|
+
font-family: var(--font-family-mono);
|
|
538
|
+
font-size: 11px;
|
|
539
|
+
gap: var(--spacing-1);
|
|
540
|
+
min-width: 0;
|
|
541
|
+
overflow-wrap: anywhere;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
.synthetic-generator-sqlite-type span {
|
|
545
|
+
color: rgb(var(--rgb-primary) / 0.78);
|
|
546
|
+
font-size: 10px;
|
|
547
|
+
letter-spacing: 0.14em;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.synthetic-generator-options-grid {
|
|
551
|
+
display: grid;
|
|
552
|
+
gap: var(--spacing-2);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
.synthetic-generator-options-grid--two {
|
|
556
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
.synthetic-generator-options-grid--three {
|
|
560
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.synthetic-generator-options-grid--timestamp {
|
|
564
|
+
grid-template-columns: minmax(0, 1fr);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
.synthetic-generator-preview {
|
|
568
|
+
border: 1px solid var(--border-subtle);
|
|
569
|
+
overflow: hidden;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
.synthetic-generator-preview table {
|
|
573
|
+
border-collapse: collapse;
|
|
574
|
+
table-layout: fixed;
|
|
575
|
+
width: 100%;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
.synthetic-generator-preview th,
|
|
579
|
+
.synthetic-generator-preview td {
|
|
580
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
581
|
+
overflow: hidden;
|
|
582
|
+
padding: var(--spacing-2) var(--spacing-4);
|
|
583
|
+
text-align: left;
|
|
584
|
+
text-overflow: ellipsis;
|
|
585
|
+
white-space: nowrap;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
.synthetic-generator-preview th {
|
|
589
|
+
background: var(--color-surface-container-low);
|
|
590
|
+
color: rgb(var(--rgb-on-surface-variant) / 0.65);
|
|
591
|
+
font-family: var(--font-family-mono);
|
|
592
|
+
font-size: 10px;
|
|
593
|
+
letter-spacing: 0.16em;
|
|
594
|
+
position: sticky;
|
|
595
|
+
text-transform: uppercase;
|
|
596
|
+
top: 0;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
.synthetic-generator-preview td {
|
|
600
|
+
background: var(--color-surface-container-lowest);
|
|
601
|
+
color: var(--color-on-surface);
|
|
602
|
+
font-family: var(--font-family-mono);
|
|
603
|
+
font-size: 12px;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
@media (max-width: 720px) {
|
|
607
|
+
.synthetic-generator-count-row {
|
|
608
|
+
grid-template-columns: 1fr;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
463
612
|
.standard-button,
|
|
464
613
|
.signature-button,
|
|
465
614
|
.delete-button,
|