sqlite-hub 0.11.1 → 0.16.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 +52 -13
- package/database.sqlite +0 -0
- package/frontend/js/api.js +64 -12
- package/frontend/js/app.js +723 -37
- package/frontend/js/components/emptyState.js +42 -46
- package/frontend/js/components/modal.js +526 -2
- package/frontend/js/components/queryEditor.js +12 -3
- package/frontend/js/components/queryResults.js +79 -17
- package/frontend/js/components/rowEditorPanel.js +345 -12
- package/frontend/js/components/sidebar.js +106 -23
- package/frontend/js/components/tableDesignerEditor.js +69 -11
- package/frontend/js/store.js +437 -26
- package/frontend/js/utils/copyColumnExport.js +117 -0
- package/frontend/js/utils/exportFilenames.js +32 -0
- package/frontend/js/utils/filePathPreview.js +315 -0
- package/frontend/js/utils/format.js +1 -1
- package/frontend/js/utils/rowEditorJson.js +65 -0
- package/frontend/js/utils/sqlFormatter.js +691 -0
- package/frontend/js/utils/tableDesigner.js +178 -6
- package/frontend/js/utils/textCellStats.js +20 -0
- package/frontend/js/utils/timestampPreview.js +264 -0
- package/frontend/js/views/charts.js +3 -1
- package/frontend/js/views/data.js +39 -4
- package/frontend/js/views/editor.js +50 -1
- package/frontend/js/views/settings.js +1 -4
- package/frontend/js/views/structure.js +154 -212
- package/frontend/styles/base.css +6 -0
- package/frontend/styles/components.css +463 -2
- package/frontend/styles/structure-graph.css +0 -3
- package/frontend/styles/tailwind.generated.css +1 -1
- package/frontend/styles/tokens.css +95 -95
- package/package.json +2 -3
- package/server/routes/export.js +97 -12
- package/server/services/sqlite/dataBrowserService.js +2 -68
- package/server/services/sqlite/exportService.js +74 -15
- package/server/services/sqlite/introspection.js +209 -1
- package/server/services/sqlite/sqlExecutor.js +31 -0
- package/server/services/sqlite/tableDesigner/changeAnalysis.js +25 -1
- package/server/services/sqlite/tableDesigner/schemaMapping.js +105 -10
- package/server/services/sqlite/tableDesigner/validation.js +60 -2
- package/server/services/sqlite/tableDesignerService.js +1 -1
- package/server/services/sqlite/tableFilter.js +75 -0
- package/server/utils/csv.js +30 -4
- package/tests/check-constraint-options.test.js +90 -0
- package/tests/export-filenames.test.js +34 -0
- package/tests/file-path-preview.test.js +165 -0
- package/tests/row-editor-json.test.js +82 -0
- package/tests/row-editor-timestamp-preview.test.js +192 -0
- package/tests/sql-formatter.test.js +173 -0
- package/tests/sql-highlight.test.js +38 -0
- package/tests/table-designer-v2-unique-constraints.test.js +78 -0
- package/tests/text-cell-stats.test.js +38 -0
|
@@ -143,6 +143,35 @@ function getUniqueResultColumns(columns = []) {
|
|
|
143
143
|
return uniqueColumns;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
function getEditorColumnTypeBadge(column) {
|
|
147
|
+
if (column.sourceColumn === 'rowid') {
|
|
148
|
+
return 'ROWID';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return String(column.declaredType || column.affinity || 'BLOB')
|
|
152
|
+
.trim()
|
|
153
|
+
.toUpperCase();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function getEditorColumnBadges(column) {
|
|
157
|
+
const badges = [{ label: getEditorColumnTypeBadge(column), tone: 'type' }];
|
|
158
|
+
|
|
159
|
+
if (Number(column.primaryKeyPosition ?? 0) > 0) {
|
|
160
|
+
badges.push({
|
|
161
|
+
label: Number(column.primaryKeyPosition) > 1 ? `PK ${column.primaryKeyPosition}` : 'PK',
|
|
162
|
+
tone: 'primary-key',
|
|
163
|
+
});
|
|
164
|
+
} else if (column.identity && column.sourceColumn === 'rowid') {
|
|
165
|
+
badges.push({ label: 'ROWID', tone: 'primary-key' });
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (column.foreignKey) {
|
|
169
|
+
badges.push({ label: 'FK', tone: 'foreign-key' });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return badges;
|
|
173
|
+
}
|
|
174
|
+
|
|
146
175
|
function renderEditorRowPanel(state) {
|
|
147
176
|
const result = state.editor.result;
|
|
148
177
|
const rowIndex = state.editor.selectedRowIndex;
|
|
@@ -153,6 +182,15 @@ function renderEditorRowPanel(state) {
|
|
|
153
182
|
}
|
|
154
183
|
|
|
155
184
|
const uniqueColumns = getUniqueResultColumns(result.editing?.columns ?? []);
|
|
185
|
+
const tableMeta =
|
|
186
|
+
result.editing?.tableMeta ?? {
|
|
187
|
+
columns: uniqueColumns.map(column => ({
|
|
188
|
+
name: column.sourceColumn,
|
|
189
|
+
primaryKeyPosition: Number(column.primaryKeyPosition ?? 0),
|
|
190
|
+
foreignKey: Boolean(column.foreignKey),
|
|
191
|
+
})),
|
|
192
|
+
foreignKeys: [],
|
|
193
|
+
};
|
|
156
194
|
const editableColumns = uniqueColumns.filter(column => {
|
|
157
195
|
if (column.identity || column.generated || !column.visible) {
|
|
158
196
|
return false;
|
|
@@ -193,20 +231,29 @@ function renderEditorRowPanel(state) {
|
|
|
193
231
|
return {
|
|
194
232
|
name: column.sourceColumn,
|
|
195
233
|
label: column.sourceColumn,
|
|
234
|
+
badges: getEditorColumnBadges(column),
|
|
235
|
+
allowedValues: column.allowedValues ?? [],
|
|
236
|
+
notNull: Boolean(column.notNull),
|
|
196
237
|
value: value === null || value === undefined ? '' : String(value),
|
|
197
238
|
};
|
|
198
239
|
}),
|
|
199
240
|
readonlyFields: readonlyColumns.map(column => ({
|
|
200
241
|
name: column.sourceColumn,
|
|
201
|
-
label:
|
|
242
|
+
label: {
|
|
243
|
+
label: column.sourceColumn,
|
|
244
|
+
badges: getEditorColumnBadges(column),
|
|
245
|
+
},
|
|
246
|
+
rawValue: row[column.resultName],
|
|
202
247
|
value: formatCellValue(row[column.resultName]),
|
|
203
248
|
})),
|
|
249
|
+
tableMeta,
|
|
204
250
|
saveError: state.editor.saveError,
|
|
205
251
|
saving: state.editor.saving,
|
|
206
252
|
deleting: state.editor.deleting,
|
|
207
253
|
deleteAction: 'delete-editor-row',
|
|
208
254
|
deleteRowIndex: rowIndex,
|
|
209
255
|
deleteEnabled: editingState.enabled && Boolean(row.__identity),
|
|
256
|
+
jsonActionsEnabled: true,
|
|
210
257
|
});
|
|
211
258
|
}
|
|
212
259
|
|
|
@@ -231,6 +278,8 @@ function renderResultsSurface(state, isResultsRoute) {
|
|
|
231
278
|
editable: editingState.enabled,
|
|
232
279
|
sortColumn: state.editor.resultSortColumn,
|
|
233
280
|
sortDirection: state.editor.resultSortDirection,
|
|
281
|
+
resultScope: 'editor',
|
|
282
|
+
sortAction: 'sort-editor-results-column',
|
|
234
283
|
})
|
|
235
284
|
: renderMissingDatabase();
|
|
236
285
|
}
|
|
@@ -34,14 +34,11 @@ function renderSettingsContent(state) {
|
|
|
34
34
|
<div class="space-y-5 p-6">
|
|
35
35
|
<div>
|
|
36
36
|
<div class="text-[10px] font-mono uppercase tracking-[0.2em] text-on-surface-variant/60">
|
|
37
|
-
|
|
37
|
+
Current_Version
|
|
38
38
|
</div>
|
|
39
39
|
<div class="mt-2 font-headline text-4xl font-black uppercase tracking-tight text-primary-container">
|
|
40
40
|
v${escapeHtml(state.settings.appVersion ?? '0.0.0')}
|
|
41
41
|
</div>
|
|
42
|
-
<div class="mt-2 text-sm text-on-surface-variant/70">
|
|
43
|
-
Read directly from <code>package.json</code>.
|
|
44
|
-
</div>
|
|
45
42
|
</div>
|
|
46
43
|
</div>
|
|
47
44
|
</section>
|
|
@@ -1,61 +1,57 @@
|
|
|
1
|
-
import { clearInspector, renderDdlSection, renderInspector } from
|
|
2
|
-
import { escapeHtml, formatNumber } from
|
|
1
|
+
import { clearInspector, renderDdlSection, renderInspector } from '../components/structureGraph.js';
|
|
2
|
+
import { escapeHtml, formatNumber } from '../utils/format.js';
|
|
3
3
|
|
|
4
4
|
function renderEntryGroup(title, entries, activeName, options = {}) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
escapeHtml(entry.tableName || entry.type),
|
|
24
|
-
"</div>",
|
|
25
|
-
].join("")
|
|
26
|
-
: "";
|
|
5
|
+
const { compact = false, showMeta = true, emptyMessage = null } = options;
|
|
6
|
+
const entriesMarkup = entries.length
|
|
7
|
+
? [
|
|
8
|
+
'<div class="space-y-2">',
|
|
9
|
+
entries
|
|
10
|
+
.map(entry => {
|
|
11
|
+
const isActive = entry.name === activeName;
|
|
12
|
+
const entryAttributes =
|
|
13
|
+
entry.type === 'table'
|
|
14
|
+
? ['data-structure-entry-name="', escapeHtml(entry.name), '"'].join('')
|
|
15
|
+
: '';
|
|
16
|
+
const metaMarkup = showMeta
|
|
17
|
+
? [
|
|
18
|
+
'<div class="mt-1 text-[10px] uppercase tracking-[0.16em] text-on-surface-variant/45">',
|
|
19
|
+
escapeHtml(entry.tableName || entry.type),
|
|
20
|
+
'</div>',
|
|
21
|
+
].join('')
|
|
22
|
+
: '';
|
|
27
23
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
24
|
+
return [
|
|
25
|
+
'<button class="w-full border px-3 ',
|
|
26
|
+
compact ? 'py-2.5' : 'py-3',
|
|
27
|
+
' text-left transition-colors ',
|
|
28
|
+
isActive
|
|
29
|
+
? 'border-primary-container/30 bg-surface-container-high'
|
|
30
|
+
: 'border-outline-variant/10 bg-surface-container-lowest hover:bg-surface-container-high',
|
|
31
|
+
'" data-action="select-structure-entry" data-entry-name="',
|
|
32
|
+
escapeHtml(entry.name),
|
|
33
|
+
'" ',
|
|
34
|
+
entryAttributes,
|
|
35
|
+
' type="button">',
|
|
36
|
+
'<div class="font-mono text-xs ',
|
|
37
|
+
isActive ? 'text-primary-container' : 'text-on-surface',
|
|
38
|
+
'">',
|
|
39
|
+
escapeHtml(entry.name),
|
|
40
|
+
'</div>',
|
|
41
|
+
metaMarkup,
|
|
42
|
+
'</button>',
|
|
43
|
+
].join('');
|
|
44
|
+
})
|
|
45
|
+
.join(''),
|
|
46
|
+
'</div>',
|
|
47
|
+
].join('')
|
|
48
|
+
: [
|
|
49
|
+
'<div class="text-sm text-on-surface-variant/45">No ',
|
|
50
|
+
emptyMessage ? escapeHtml(emptyMessage) : `${escapeHtml(title.toLowerCase())} found.`,
|
|
51
|
+
'</div>',
|
|
52
|
+
].join('');
|
|
57
53
|
|
|
58
|
-
|
|
54
|
+
return `
|
|
59
55
|
<section class="structure-sidebar__section">
|
|
60
56
|
<div class="mb-4 shrink-0 text-[10px] font-bold uppercase tracking-[0.25em] text-primary-container">
|
|
61
57
|
${escapeHtml(title)}
|
|
@@ -66,19 +62,19 @@ function renderEntryGroup(title, entries, activeName, options = {}) {
|
|
|
66
62
|
}
|
|
67
63
|
|
|
68
64
|
function getFilteredTables(tables, searchQuery) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
const normalizedSearch = String(searchQuery ?? '')
|
|
66
|
+
.trim()
|
|
67
|
+
.toLowerCase();
|
|
72
68
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
69
|
+
if (!normalizedSearch) {
|
|
70
|
+
return tables;
|
|
71
|
+
}
|
|
76
72
|
|
|
77
|
-
|
|
73
|
+
return tables.filter(table => table.name.toLowerCase().includes(normalizedSearch));
|
|
78
74
|
}
|
|
79
75
|
|
|
80
76
|
function renderStructureTableSearch(state) {
|
|
81
|
-
|
|
77
|
+
return `
|
|
82
78
|
<label class="table-designer-sidebar__search">
|
|
83
79
|
<span class="material-symbols-outlined text-sm text-on-surface-variant/55">search</span>
|
|
84
80
|
<input
|
|
@@ -87,14 +83,14 @@ function renderStructureTableSearch(state) {
|
|
|
87
83
|
placeholder="Search tables..."
|
|
88
84
|
spellcheck="false"
|
|
89
85
|
type="search"
|
|
90
|
-
value="${escapeHtml(state.structure.tableSearchQuery ??
|
|
86
|
+
value="${escapeHtml(state.structure.tableSearchQuery ?? '')}"
|
|
91
87
|
/>
|
|
92
88
|
</label>
|
|
93
89
|
`;
|
|
94
90
|
}
|
|
95
91
|
|
|
96
92
|
function renderLoadingInspector() {
|
|
97
|
-
|
|
93
|
+
return `
|
|
98
94
|
<div class="structure-graph__panel is-empty">
|
|
99
95
|
<span class="material-symbols-outlined structure-graph__empty-icon">progress_activity</span>
|
|
100
96
|
<div class="structure-graph__title">Loading</div>
|
|
@@ -106,31 +102,31 @@ function renderLoadingInspector() {
|
|
|
106
102
|
}
|
|
107
103
|
|
|
108
104
|
function renderObjectInspector(detail) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
if (!detail) {
|
|
106
|
+
return clearInspector();
|
|
107
|
+
}
|
|
112
108
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
109
|
+
const visibleColumns = (detail.columns ?? []).filter(column => column.visible !== false);
|
|
110
|
+
const foreignKeyLinks = (detail.foreignKeys ?? []).reduce(
|
|
111
|
+
(count, foreignKey) => count + (foreignKey.mappings?.length ?? 0),
|
|
112
|
+
0,
|
|
113
|
+
);
|
|
118
114
|
|
|
119
|
-
|
|
115
|
+
return `
|
|
120
116
|
<div class="structure-graph__panel">
|
|
121
117
|
<div class="space-y-3">
|
|
122
118
|
<div class="structure-graph__eyebrow">Object Inspector</div>
|
|
123
119
|
<div class="structure-graph__title">${escapeHtml(detail.name)}</div>
|
|
124
120
|
<div class="flex flex-wrap items-center gap-3">
|
|
125
|
-
<div class="structure-graph__subtitle">${escapeHtml(detail.type ??
|
|
121
|
+
<div class="structure-graph__subtitle">${escapeHtml(detail.type ?? 'object')}</div>
|
|
126
122
|
${
|
|
127
|
-
|
|
128
|
-
|
|
123
|
+
detail.tableName
|
|
124
|
+
? `
|
|
129
125
|
<div class="structure-graph__subtitle">
|
|
130
126
|
TABLE ${escapeHtml(detail.tableName)}
|
|
131
127
|
</div>
|
|
132
128
|
`
|
|
133
|
-
|
|
129
|
+
: ''
|
|
134
130
|
}
|
|
135
131
|
</div>
|
|
136
132
|
</div>
|
|
@@ -138,70 +134,60 @@ function renderObjectInspector(detail) {
|
|
|
138
134
|
<div class="structure-graph__summary">
|
|
139
135
|
<div class="structure-graph__summary-card">
|
|
140
136
|
<div class="structure-graph__summary-label">Columns</div>
|
|
141
|
-
<div class="structure-graph__summary-value">${escapeHtml(
|
|
142
|
-
formatNumber(visibleColumns.length)
|
|
143
|
-
)}</div>
|
|
137
|
+
<div class="structure-graph__summary-value">${escapeHtml(formatNumber(visibleColumns.length))}</div>
|
|
144
138
|
</div>
|
|
145
139
|
<div class="structure-graph__summary-card">
|
|
146
140
|
<div class="structure-graph__summary-label">FK Links</div>
|
|
147
|
-
<div class="structure-graph__summary-value">${escapeHtml(
|
|
148
|
-
formatNumber(foreignKeyLinks)
|
|
149
|
-
)}</div>
|
|
141
|
+
<div class="structure-graph__summary-value">${escapeHtml(formatNumber(foreignKeyLinks))}</div>
|
|
150
142
|
</div>
|
|
151
143
|
<div class="structure-graph__summary-card">
|
|
152
144
|
<div class="structure-graph__summary-label">Indexes</div>
|
|
153
|
-
<div class="structure-graph__summary-value">${escapeHtml(
|
|
154
|
-
formatNumber(detail.indexes?.length ?? 0)
|
|
155
|
-
)}</div>
|
|
145
|
+
<div class="structure-graph__summary-value">${escapeHtml(formatNumber(detail.indexes?.length ?? 0))}</div>
|
|
156
146
|
</div>
|
|
157
147
|
<div class="structure-graph__summary-card">
|
|
158
148
|
<div class="structure-graph__summary-label">Triggers</div>
|
|
159
|
-
<div class="structure-graph__summary-value">${escapeHtml(
|
|
160
|
-
formatNumber(detail.triggers?.length ?? 0)
|
|
161
|
-
)}</div>
|
|
149
|
+
<div class="structure-graph__summary-value">${escapeHtml(formatNumber(detail.triggers?.length ?? 0))}</div>
|
|
162
150
|
</div>
|
|
163
151
|
</div>
|
|
164
152
|
|
|
165
153
|
${
|
|
166
|
-
|
|
167
|
-
|
|
154
|
+
visibleColumns.length
|
|
155
|
+
? `
|
|
168
156
|
<section class="structure-graph__section">
|
|
169
157
|
<div class="structure-graph__section-title">Columns</div>
|
|
170
158
|
<div class="structure-graph__column-list">
|
|
171
159
|
${visibleColumns
|
|
172
|
-
|
|
173
|
-
|
|
160
|
+
.map(
|
|
161
|
+
column => `
|
|
174
162
|
<div class="structure-graph__column-row">
|
|
175
163
|
<div class="min-w-0 space-y-1">
|
|
176
|
-
<div class="structure-graph__column-name">${escapeHtml(
|
|
177
|
-
column.name
|
|
178
|
-
)}</div>
|
|
164
|
+
<div class="structure-graph__column-name">${escapeHtml(column.name)}</div>
|
|
179
165
|
<div class="structure-graph__column-type">${escapeHtml(
|
|
180
|
-
|
|
166
|
+
column.declaredType || column.affinity || 'BLOB',
|
|
181
167
|
)}</div>
|
|
182
168
|
</div>
|
|
183
169
|
<div class="structure-graph__column-flags">
|
|
184
170
|
${
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
171
|
+
column.primaryKeyPosition > 0
|
|
172
|
+
? `<span class="structure-graph__flag is-key">PK${
|
|
173
|
+
column.primaryKeyPosition > 1
|
|
174
|
+
? ` ${escapeHtml(String(column.primaryKeyPosition))}`
|
|
175
|
+
: ''
|
|
176
|
+
}</span>`
|
|
177
|
+
: ''
|
|
192
178
|
}
|
|
193
179
|
<span class="structure-graph__flag is-nullable">${
|
|
194
|
-
|
|
180
|
+
column.notNull ? 'NOT NULL' : 'NULLABLE'
|
|
195
181
|
}</span>
|
|
196
182
|
</div>
|
|
197
183
|
</div>
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
184
|
+
`,
|
|
185
|
+
)
|
|
186
|
+
.join('')}
|
|
201
187
|
</div>
|
|
202
188
|
</section>
|
|
203
189
|
`
|
|
204
|
-
|
|
190
|
+
: ''
|
|
205
191
|
}
|
|
206
192
|
|
|
207
193
|
${renderDdlSection(detail.ddl)}
|
|
@@ -210,19 +196,19 @@ function renderObjectInspector(detail) {
|
|
|
210
196
|
}
|
|
211
197
|
|
|
212
198
|
function renderGraphSurface(structure, selectedName, detail, detailLoading, tablesVisible = true) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
199
|
+
const graph = structure?.graph ?? { tables: [], relationshipCount: 0 };
|
|
200
|
+
const selectedGraphTable =
|
|
201
|
+
graph.tables?.find(table => table.name === selectedName && table.type === 'table') ?? null;
|
|
202
|
+
const toolbarButtonClass = 'standard-button';
|
|
203
|
+
const inspectorMarkup = selectedGraphTable
|
|
204
|
+
? renderInspector(selectedGraphTable)
|
|
205
|
+
: detailLoading
|
|
206
|
+
? renderLoadingInspector()
|
|
207
|
+
: detail
|
|
208
|
+
? renderObjectInspector(detail)
|
|
209
|
+
: clearInspector();
|
|
224
210
|
|
|
225
|
-
|
|
211
|
+
return `
|
|
226
212
|
<section class="structure-graph" data-structure-graph-root>
|
|
227
213
|
<div class="structure-graph__toolbar">
|
|
228
214
|
<div class="structure-graph__toolbar-main">
|
|
@@ -231,10 +217,8 @@ function renderGraphSurface(structure, selectedName, detail, detailLoading, tabl
|
|
|
231
217
|
data-action="toggle-structure-tables"
|
|
232
218
|
type="button"
|
|
233
219
|
>
|
|
234
|
-
<span class="material-symbols-outlined text-sm">${
|
|
235
|
-
|
|
236
|
-
}</span>
|
|
237
|
-
${tablesVisible ? "Hide Tables" : "Show Tables"}
|
|
220
|
+
<span class="material-symbols-outlined text-sm">${tablesVisible ? 'visibility_off' : 'visibility'}</span>
|
|
221
|
+
${tablesVisible ? 'Hide Tables' : 'Show Tables'}
|
|
238
222
|
</button>
|
|
239
223
|
</div>
|
|
240
224
|
<div class="structure-graph__toolbar-actions">
|
|
@@ -288,7 +272,7 @@ function renderGraphSurface(structure, selectedName, detail, detailLoading, tabl
|
|
|
288
272
|
<div
|
|
289
273
|
class="structure-graph__empty"
|
|
290
274
|
data-structure-graph-empty
|
|
291
|
-
${graph.tables?.length ?
|
|
275
|
+
${graph.tables?.length ? 'hidden' : ''}
|
|
292
276
|
>
|
|
293
277
|
<span class="material-symbols-outlined structure-graph__empty-icon">account_tree</span>
|
|
294
278
|
<div class="structure-graph__title">No Tables</div>
|
|
@@ -309,46 +293,20 @@ function renderGraphSurface(structure, selectedName, detail, detailLoading, tabl
|
|
|
309
293
|
`;
|
|
310
294
|
}
|
|
311
295
|
|
|
312
|
-
function renderStructureWorkspaceHeader(structure, selectedName) {
|
|
313
|
-
const tableCount = structure?.grouped?.tables?.length ?? 0;
|
|
314
|
-
const viewCount = structure?.grouped?.views?.length ?? 0;
|
|
315
|
-
|
|
316
|
-
return `
|
|
317
|
-
<header class="structure-view__header">
|
|
318
|
-
<div class="structure-headline-container">
|
|
319
|
-
<div class="text-[10px] font-bold uppercase tracking-[0.2em] text-primary-container">
|
|
320
|
-
Schema Graph
|
|
321
|
-
</div>
|
|
322
|
-
<h1 class="mt-2 font-headline text-4xl font-black uppercase tracking-tight text-primary-container">
|
|
323
|
-
Structure
|
|
324
|
-
</h1>
|
|
325
|
-
<div class="mt-2 text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
|
|
326
|
-
${
|
|
327
|
-
selectedName
|
|
328
|
-
? `selected ${escapeHtml(selectedName)} // tables ${escapeHtml(formatNumber(tableCount))} // views ${escapeHtml(formatNumber(viewCount))}`
|
|
329
|
-
: `tables ${escapeHtml(formatNumber(tableCount))} // views ${escapeHtml(formatNumber(viewCount))}`
|
|
330
|
-
}
|
|
331
|
-
</div>
|
|
332
|
-
</div>
|
|
333
|
-
</header>
|
|
334
|
-
`;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
296
|
export function renderStructureView(state) {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
const tablesVisible = state.structure.tablesVisible !== false;
|
|
297
|
+
const structure = state.structure.data;
|
|
298
|
+
const filteredTables = structure
|
|
299
|
+
? getFilteredTables(structure.grouped.tables ?? [], state.structure.tableSearchQuery)
|
|
300
|
+
: [];
|
|
301
|
+
const detail = state.structure.detail?.name === state.structure.selectedName ? state.structure.detail : null;
|
|
302
|
+
const tablesVisible = state.structure.tablesVisible !== false;
|
|
345
303
|
|
|
346
|
-
|
|
347
|
-
|
|
304
|
+
return {
|
|
305
|
+
main: `
|
|
348
306
|
<section class="view-surface structure-view">
|
|
349
307
|
${
|
|
350
|
-
|
|
351
|
-
|
|
308
|
+
tablesVisible
|
|
309
|
+
? `
|
|
352
310
|
<aside class="structure-view__sidebar">
|
|
353
311
|
<div class="structure-view__sidebar-header">
|
|
354
312
|
<div class="text-[10px] font-bold uppercase tracking-[0.2em] text-primary-container">
|
|
@@ -356,62 +314,46 @@ export function renderStructureView(state) {
|
|
|
356
314
|
</div>
|
|
357
315
|
<div class="mt-2 text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
|
|
358
316
|
total ${escapeHtml(
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
317
|
+
formatNumber(
|
|
318
|
+
(structure?.grouped?.tables?.length ?? 0) +
|
|
319
|
+
(structure?.grouped?.views?.length ?? 0) +
|
|
320
|
+
(structure?.grouped?.indexes?.length ?? 0) +
|
|
321
|
+
(structure?.grouped?.triggers?.length ?? 0),
|
|
322
|
+
),
|
|
365
323
|
)}
|
|
366
324
|
</div>
|
|
367
325
|
</div>
|
|
368
326
|
${renderStructureTableSearch(state)}
|
|
369
327
|
<div class="structure-view__sidebar-body custom-scrollbar">
|
|
370
328
|
${
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
${renderEntryGroup(
|
|
374
|
-
"Tables",
|
|
375
|
-
filteredTables,
|
|
376
|
-
state.structure.selectedName,
|
|
377
|
-
{
|
|
329
|
+
structure
|
|
330
|
+
? `
|
|
331
|
+
${renderEntryGroup('Tables', filteredTables, state.structure.selectedName, {
|
|
378
332
|
compact: true,
|
|
379
333
|
showMeta: false,
|
|
380
334
|
emptyMessage: state.structure.tableSearchQuery
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
)}
|
|
385
|
-
${renderEntryGroup(
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
)}
|
|
390
|
-
${renderEntryGroup(
|
|
391
|
-
"Indexes",
|
|
392
|
-
structure.grouped.indexes,
|
|
393
|
-
state.structure.selectedName,
|
|
394
|
-
{ compact: true, showMeta: false }
|
|
395
|
-
)}
|
|
396
|
-
${renderEntryGroup(
|
|
397
|
-
"Triggers",
|
|
398
|
-
structure.grouped.triggers,
|
|
399
|
-
state.structure.selectedName
|
|
400
|
-
)}
|
|
335
|
+
? 'tables match the current search.'
|
|
336
|
+
: null,
|
|
337
|
+
})}
|
|
338
|
+
${renderEntryGroup('Views', structure.grouped.views, state.structure.selectedName)}
|
|
339
|
+
${renderEntryGroup('Indexes', structure.grouped.indexes, state.structure.selectedName, {
|
|
340
|
+
compact: true,
|
|
341
|
+
showMeta: false,
|
|
342
|
+
})}
|
|
343
|
+
${renderEntryGroup('Triggers', structure.grouped.triggers, state.structure.selectedName)}
|
|
401
344
|
`
|
|
402
|
-
|
|
345
|
+
: ''
|
|
403
346
|
}
|
|
404
347
|
</div>
|
|
405
348
|
</aside>
|
|
406
349
|
`
|
|
407
|
-
|
|
350
|
+
: ''
|
|
408
351
|
}
|
|
409
352
|
|
|
410
353
|
<section class="structure-view__detail">
|
|
411
|
-
${renderStructureWorkspaceHeader(structure, state.structure.selectedName)}
|
|
412
354
|
${
|
|
413
|
-
|
|
414
|
-
|
|
355
|
+
state.structure.loading && !structure
|
|
356
|
+
? `
|
|
415
357
|
<div class="flex min-h-0 flex-1 items-center justify-center border-t border-outline-variant/10 bg-surface-container-low">
|
|
416
358
|
<div class="text-center text-on-surface-variant/40">
|
|
417
359
|
<span class="material-symbols-outlined mb-3 text-4xl">progress_activity</span>
|
|
@@ -419,8 +361,8 @@ export function renderStructureView(state) {
|
|
|
419
361
|
</div>
|
|
420
362
|
</div>
|
|
421
363
|
`
|
|
422
|
-
|
|
423
|
-
|
|
364
|
+
: state.structure.error
|
|
365
|
+
? `
|
|
424
366
|
<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">
|
|
425
367
|
<div class="font-headline text-xs font-bold uppercase tracking-[0.18em] text-error">
|
|
426
368
|
${escapeHtml(state.structure.error.code)}
|
|
@@ -428,23 +370,23 @@ export function renderStructureView(state) {
|
|
|
428
370
|
<div class="mt-2">${escapeHtml(state.structure.error.message)}</div>
|
|
429
371
|
</div>
|
|
430
372
|
`
|
|
431
|
-
|
|
432
|
-
|
|
373
|
+
: structure
|
|
374
|
+
? `
|
|
433
375
|
<div class="structure-view__graph-shell">
|
|
434
376
|
${renderGraphSurface(
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
377
|
+
structure,
|
|
378
|
+
state.structure.selectedName,
|
|
379
|
+
detail,
|
|
380
|
+
state.structure.detailLoading,
|
|
381
|
+
tablesVisible,
|
|
440
382
|
)}
|
|
441
383
|
</div>
|
|
442
384
|
`
|
|
443
|
-
|
|
385
|
+
: ''
|
|
444
386
|
}
|
|
445
387
|
</section>
|
|
446
388
|
</section>
|
|
447
389
|
`,
|
|
448
|
-
|
|
449
|
-
|
|
390
|
+
panel: '',
|
|
391
|
+
};
|
|
450
392
|
}
|
package/frontend/styles/base.css
CHANGED