sqlite-hub 2.0.1 → 2.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 +14 -9
- package/docs/MCP.md +8 -0
- package/docs/changelog.md +10 -0
- package/docs/guidelines/DESIGN.md +64 -0
- package/frontend/assets/mockups/backups_1_1920.webp +0 -0
- package/frontend/assets/mockups/backups_2_compare_drawer_1920.webp +0 -0
- package/frontend/assets/mockups/backups_3_create_backup_modal_1920.webp +0 -0
- package/frontend/assets/mockups/backups_4_edit_backup_modal_1920.webp +0 -0
- package/frontend/assets/mockups/backups_5_restore_backup_modal_1920.webp +0 -0
- package/frontend/assets/mockups/backups_6_delete_backup_modal_1920.webp +0 -0
- package/frontend/assets/mockups/charts_1_1920.webp +0 -0
- package/frontend/assets/mockups/charts_2_query_detail_1920.webp +0 -0
- package/frontend/assets/mockups/charts_3_create_query_chart_modal_1920.webp +0 -0
- package/frontend/assets/mockups/charts_4_edit_query_chart_modal_1920.webp +0 -0
- package/frontend/assets/mockups/charts_5_delete_query_chart_modal_1920.webp +0 -0
- package/frontend/assets/mockups/charts_6_copy_column_modal_1920.webp +0 -0
- package/frontend/assets/mockups/connections_1_1920.webp +0 -0
- package/frontend/assets/mockups/connections_2_create_connection_modal_1920.webp +0 -0
- package/frontend/assets/mockups/connections_3_open_connection_modal_1920.webp +0 -0
- package/frontend/assets/mockups/data_1_1920.webp +0 -0
- package/frontend/assets/mockups/data_2_roweditor_1920.webp +0 -0
- package/frontend/assets/mockups/data_3_generate_data_modal_1920.webp +0 -0
- package/frontend/assets/mockups/data_4_data_export_modal_1920.webp +0 -0
- package/frontend/assets/mockups/documents_1_1920.webp +0 -0
- package/frontend/assets/mockups/documents_2_document_insert_table_modal_1920.webp +0 -0
- package/frontend/assets/mockups/documents_3_document_insert_note_modal_1920.webp +0 -0
- package/frontend/assets/mockups/logs_1_1920.webp +0 -0
- package/frontend/assets/mockups/media_tagging_queue_1_1920.webp +0 -0
- package/frontend/assets/mockups/media_tagging_setup_1_1920.webp +0 -0
- package/frontend/assets/mockups/media_tagging_setup_2_create_media_tagging_tag_table_modal_1920.webp +0 -0
- package/frontend/assets/mockups/media_tagging_setup_3_create_media_tagging_mapping_table_modal_1920.webp +0 -0
- package/frontend/assets/mockups/overview_1_1920.webp +0 -0
- package/frontend/assets/mockups/settings_1_1920.webp +0 -0
- package/frontend/assets/mockups/sql_editor_1_1920.webp +0 -0
- package/frontend/assets/mockups/sql_editor_2_query_detail_1920.webp +0 -0
- package/frontend/assets/mockups/sql_editor_3_query_export_modal_1920.webp +0 -0
- package/frontend/assets/mockups/structure_1_1920.webp +0 -0
- package/frontend/assets/mockups/structure_2_generate_types_modal_1920.webp +0 -0
- package/frontend/assets/mockups/structure_3_generate_types_modal_1920.webp +0 -0
- package/frontend/assets/mockups/table_designer_1_1920.webp +0 -0
- package/frontend/assets/mockups/table_designer_2_table_designer_constraints_modal_1920.webp +0 -0
- package/frontend/js/api.js +11 -0
- package/frontend/js/app.js +238 -8
- package/frontend/js/components/badges.js +16 -0
- package/frontend/js/components/connectionCard.js +35 -0
- package/frontend/js/components/modal.js +116 -245
- package/frontend/js/components/queryHistoryDetail.js +25 -45
- package/frontend/js/components/rowEditorPanel.js +7 -60
- package/frontend/js/components/structureGraph.js +292 -13
- package/frontend/js/components/tableDesignerConstraintsDrawer.js +400 -0
- package/frontend/js/components/tableDesignerEditor.js +51 -16
- package/frontend/js/components/tableDesignerSidebar.js +9 -5
- package/frontend/js/store.js +551 -7
- package/frontend/js/utils/connectionRegistry.js +76 -0
- package/frontend/js/utils/tableDesigner.js +431 -62
- package/frontend/js/views/backups.js +2 -5
- package/frontend/js/views/charts.js +20 -10
- package/frontend/js/views/connections.js +103 -3
- package/frontend/js/views/data.js +19 -5
- package/frontend/js/views/structure.js +28 -5
- package/frontend/js/views/tableAdvisor.js +14 -6
- package/frontend/js/views/tableDesigner.js +44 -9
- package/frontend/styles/components.css +676 -79
- package/frontend/styles/structure-graph.css +49 -0
- package/frontend/styles/tailwind.generated.css +1 -1
- package/package.json +1 -1
- package/server/routes/connections.js +12 -0
- package/server/routes/data.js +1 -1
- package/server/routes/tableDesigner.js +15 -1
- package/server/services/mcpToolService.js +28 -0
- package/server/services/sqlite/connectionManager.js +23 -2
- package/server/services/sqlite/dataBrowserService.js +21 -0
- package/server/services/sqlite/introspection.js +39 -1
- package/server/services/sqlite/structureService.js +66 -11
- package/server/services/sqlite/tableAdvisor.js +6 -0
- package/server/services/sqlite/tableDesigner/schemaMapping.js +7 -1
- package/server/services/sqlite/tableDesigner/sql.js +31 -2
- package/server/services/sqlite/tableDesigner/validation.js +69 -9
- package/server/services/sqlite/tableDesignerService.js +110 -5
- package/server/services/storage/appStateStore.js +303 -4
- package/frontend/assets/mockups/backups_2_create_backup_modal_1920.webp +0 -0
- package/frontend/assets/mockups/backups_3_edit_backup_modal_1920.webp +0 -0
- package/frontend/assets/mockups/backups_4_restore_backup_modal_1920.webp +0 -0
- package/frontend/assets/mockups/backups_5_delete_backup_modal_1920.webp +0 -0
- package/frontend/assets/mockups/data_3_data_export_modal_1920.webp +0 -0
|
@@ -18,6 +18,11 @@ import {
|
|
|
18
18
|
MEDIA_TAGGING_DEFAULT_TAG_TABLE_SQL,
|
|
19
19
|
} from '../lib/mediaTaggingDefaults.js';
|
|
20
20
|
import { SYNTHETIC_GENERATOR_TYPES, getSyntheticGeneratorLabel } from '../utils/syntheticData.js';
|
|
21
|
+
import {
|
|
22
|
+
MAX_CONNECTION_TAG_NAME_LENGTH,
|
|
23
|
+
normalizeConnectionTagKey,
|
|
24
|
+
normalizeConnectionTagName,
|
|
25
|
+
} from '../utils/connectionRegistry.js';
|
|
21
26
|
|
|
22
27
|
function renderField({ label, name, type = 'text', placeholder = '', value = '' }) {
|
|
23
28
|
return `
|
|
@@ -192,7 +197,114 @@ export function renderOpenConnectionForm(modal) {
|
|
|
192
197
|
`;
|
|
193
198
|
}
|
|
194
199
|
|
|
195
|
-
function
|
|
200
|
+
function renderEditConnectionTagEditor(modal, state) {
|
|
201
|
+
const assignedTags = Array.isArray(modal.assignedTags) ? modal.assignedTags : [];
|
|
202
|
+
const assignedKeys = new Set(assignedTags.map(tag => normalizeConnectionTagKey(tag.name)));
|
|
203
|
+
const tagQuery = String(modal.tagQuery ?? '');
|
|
204
|
+
const normalizedQuery = normalizeConnectionTagName(tagQuery);
|
|
205
|
+
const queryKey = normalizeConnectionTagKey(normalizedQuery);
|
|
206
|
+
const matchingTags = (state.connections.tags ?? [])
|
|
207
|
+
.filter(tag => !assignedKeys.has(normalizeConnectionTagKey(tag.name)))
|
|
208
|
+
.filter(tag => {
|
|
209
|
+
if (!queryKey) {
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return normalizeConnectionTagKey(tag.name).includes(queryKey);
|
|
214
|
+
})
|
|
215
|
+
.slice(0, 6);
|
|
216
|
+
const exactMatch = (state.connections.tags ?? []).some(tag => normalizeConnectionTagKey(tag.name) === queryKey);
|
|
217
|
+
const canCreate = Boolean(normalizedQuery && !exactMatch);
|
|
218
|
+
const assignedMarkup = assignedTags.length
|
|
219
|
+
? assignedTags
|
|
220
|
+
.map(
|
|
221
|
+
tag => `
|
|
222
|
+
<span class="connection-edit-tag-chip">
|
|
223
|
+
<span class="connection-edit-tag-chip__label">${escapeHtml(tag.name)}</span>
|
|
224
|
+
<button
|
|
225
|
+
aria-label="Remove ${escapeHtml(tag.name)} tag"
|
|
226
|
+
class="connection-edit-tag-chip__remove"
|
|
227
|
+
data-action="remove-edit-connection-tag"
|
|
228
|
+
data-tag-name="${escapeHtml(tag.name)}"
|
|
229
|
+
type="button"
|
|
230
|
+
title="Remove tag"
|
|
231
|
+
>
|
|
232
|
+
<span aria-hidden="true">×</span>
|
|
233
|
+
</button>
|
|
234
|
+
<input name="tags" type="hidden" value="${escapeHtml(tag.name)}" />
|
|
235
|
+
</span>
|
|
236
|
+
`,
|
|
237
|
+
)
|
|
238
|
+
.join('')
|
|
239
|
+
: '<div class="connection-edit-tags__empty">NO_TAGS_ASSIGNED</div>';
|
|
240
|
+
const matchMarkup = matchingTags
|
|
241
|
+
.map(
|
|
242
|
+
tag => `
|
|
243
|
+
<button
|
|
244
|
+
class="connection-edit-tag-option"
|
|
245
|
+
data-action="add-edit-connection-tag"
|
|
246
|
+
data-tag-name="${escapeHtml(tag.name)}"
|
|
247
|
+
type="button"
|
|
248
|
+
>
|
|
249
|
+
<span>${escapeHtml(tag.name)}</span>
|
|
250
|
+
<span>${escapeHtml(String(tag.connectionCount ?? 0))}</span>
|
|
251
|
+
</button>
|
|
252
|
+
`,
|
|
253
|
+
)
|
|
254
|
+
.join('');
|
|
255
|
+
|
|
256
|
+
return `
|
|
257
|
+
<div class="connection-edit-tags">
|
|
258
|
+
<div class="connection-edit-tags__label">Tags</div>
|
|
259
|
+
<div class="connection-edit-tags__assigned">
|
|
260
|
+
${assignedMarkup}
|
|
261
|
+
</div>
|
|
262
|
+
<label class="connection-edit-tags__input-shell">
|
|
263
|
+
<span class="sr-only">Add or create tag</span>
|
|
264
|
+
<input
|
|
265
|
+
autocomplete="off"
|
|
266
|
+
class="control-input connection-edit-tags__input"
|
|
267
|
+
data-bind="edit-connection-tag-query"
|
|
268
|
+
maxlength="${MAX_CONNECTION_TAG_NAME_LENGTH}"
|
|
269
|
+
placeholder="ADD_OR_CREATE_TAG..."
|
|
270
|
+
spellcheck="false"
|
|
271
|
+
type="text"
|
|
272
|
+
value="${escapeHtml(tagQuery)}"
|
|
273
|
+
/>
|
|
274
|
+
</label>
|
|
275
|
+
${
|
|
276
|
+
modal.tagError
|
|
277
|
+
? `<div class="connection-edit-tags__error">${escapeHtml(modal.tagError)}</div>`
|
|
278
|
+
: ''
|
|
279
|
+
}
|
|
280
|
+
<div class="connection-edit-tag-options">
|
|
281
|
+
${
|
|
282
|
+
matchMarkup
|
|
283
|
+
? `<div class="connection-edit-tag-options__section">MATCHING_TAGS</div>${matchMarkup}`
|
|
284
|
+
: ''
|
|
285
|
+
}
|
|
286
|
+
${
|
|
287
|
+
canCreate
|
|
288
|
+
? `
|
|
289
|
+
<div class="connection-edit-tag-options__section">CREATE_NEW</div>
|
|
290
|
+
<button
|
|
291
|
+
class="connection-edit-tag-option connection-edit-tag-option--create"
|
|
292
|
+
data-action="create-edit-connection-tag"
|
|
293
|
+
data-edit-connection-tag-primary
|
|
294
|
+
data-tag-name="${escapeHtml(normalizedQuery)}"
|
|
295
|
+
type="button"
|
|
296
|
+
>
|
|
297
|
+
<span>CREATE "${escapeHtml(normalizedQuery)}"</span>
|
|
298
|
+
</button>
|
|
299
|
+
`
|
|
300
|
+
: ''
|
|
301
|
+
}
|
|
302
|
+
</div>
|
|
303
|
+
</div>
|
|
304
|
+
`;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function renderEditConnectionForm(modal, state) {
|
|
196
308
|
const connection = modal.connection ?? {};
|
|
197
309
|
|
|
198
310
|
return `
|
|
@@ -246,6 +358,7 @@ function renderEditConnectionForm(modal) {
|
|
|
246
358
|
checked: Boolean(connection.readOnly),
|
|
247
359
|
text: 'Open read-only',
|
|
248
360
|
})}
|
|
361
|
+
${renderEditConnectionTagEditor(modal, state)}
|
|
249
362
|
${renderError(modal.error)}
|
|
250
363
|
<div class="flex items-center justify-between gap-3 pt-2">
|
|
251
364
|
<button
|
|
@@ -1926,242 +2039,6 @@ export function renderCopyColumnModal(modal, state) {
|
|
|
1926
2039
|
`;
|
|
1927
2040
|
}
|
|
1928
2041
|
|
|
1929
|
-
function getTableDesignerUniqueConstraintTypeLabel(constraint) {
|
|
1930
|
-
if (constraint.partial) {
|
|
1931
|
-
return 'Partial unique index';
|
|
1932
|
-
}
|
|
1933
|
-
|
|
1934
|
-
if ((constraint.columns?.length ?? 0) > 1) {
|
|
1935
|
-
return 'Multi-column unique';
|
|
1936
|
-
}
|
|
1937
|
-
|
|
1938
|
-
return 'Unique constraint';
|
|
1939
|
-
}
|
|
1940
|
-
|
|
1941
|
-
function renderTableDesignerUniqueConstraintExpression(constraint) {
|
|
1942
|
-
const expression = String(constraint.expression || constraint.sql || '').trim();
|
|
1943
|
-
|
|
1944
|
-
if (expression) {
|
|
1945
|
-
return expression;
|
|
1946
|
-
}
|
|
1947
|
-
|
|
1948
|
-
const columns = (constraint.columns ?? []).map(column => column.name).filter(Boolean);
|
|
1949
|
-
return columns.length ? `UNIQUE (${columns.join(', ')})` : 'UNIQUE constraint';
|
|
1950
|
-
}
|
|
1951
|
-
|
|
1952
|
-
function renderTableDesignerCheckConstraintExpression(constraint) {
|
|
1953
|
-
return String(constraint.expression || '').trim() || 'CHECK constraint';
|
|
1954
|
-
}
|
|
1955
|
-
|
|
1956
|
-
function normalizeTableDesignerConstraintColumnName(name) {
|
|
1957
|
-
return String(name ?? '')
|
|
1958
|
-
.trim()
|
|
1959
|
-
.toLowerCase();
|
|
1960
|
-
}
|
|
1961
|
-
|
|
1962
|
-
function tableDesignerConstraintIncludesColumn(constraint, columnName) {
|
|
1963
|
-
const normalizedColumn = normalizeTableDesignerConstraintColumnName(columnName);
|
|
1964
|
-
|
|
1965
|
-
if (!normalizedColumn) {
|
|
1966
|
-
return false;
|
|
1967
|
-
}
|
|
1968
|
-
|
|
1969
|
-
return (constraint.columns ?? []).some(
|
|
1970
|
-
constraintColumn => normalizeTableDesignerConstraintColumnName(constraintColumn.name) === normalizedColumn,
|
|
1971
|
-
);
|
|
1972
|
-
}
|
|
1973
|
-
|
|
1974
|
-
function renderTableDesignerUniqueConstraintEditor(constraint) {
|
|
1975
|
-
const columns = (constraint.columns ?? []).map(column => column.name).filter(Boolean);
|
|
1976
|
-
|
|
1977
|
-
return `
|
|
1978
|
-
<article class="table-designer-constraint-editor">
|
|
1979
|
-
<div class="table-designer-constraint-editor__header">
|
|
1980
|
-
<div class="min-w-0">
|
|
1981
|
-
<div class="table-designer-constraint-editor__type">
|
|
1982
|
-
${escapeHtml(getTableDesignerUniqueConstraintTypeLabel(constraint))}
|
|
1983
|
-
${constraint.origin ? ` // origin ${escapeHtml(constraint.origin)}` : ''}
|
|
1984
|
-
</div>
|
|
1985
|
-
<input
|
|
1986
|
-
class="table-designer-field table-designer-constraint-editor__name"
|
|
1987
|
-
data-bind="table-designer-constraint-field"
|
|
1988
|
-
data-constraint-id="${escapeHtml(constraint.id)}"
|
|
1989
|
-
data-constraint-kind="unique"
|
|
1990
|
-
data-field="name"
|
|
1991
|
-
spellcheck="false"
|
|
1992
|
-
type="text"
|
|
1993
|
-
value="${escapeHtml(constraint.name || 'UNIQUE constraint')}"
|
|
1994
|
-
/>
|
|
1995
|
-
</div>
|
|
1996
|
-
<div class="status-badge status-badge--muted">Rebuild</div>
|
|
1997
|
-
</div>
|
|
1998
|
-
${
|
|
1999
|
-
columns.length
|
|
2000
|
-
? `
|
|
2001
|
-
<div class="table-designer-constraint__columns">
|
|
2002
|
-
${columns.map(column => `<span>${escapeHtml(column)}</span>`).join('')}
|
|
2003
|
-
</div>
|
|
2004
|
-
`
|
|
2005
|
-
: ''
|
|
2006
|
-
}
|
|
2007
|
-
<textarea
|
|
2008
|
-
class="table-designer-constraint-editor__sql custom-scrollbar"
|
|
2009
|
-
data-bind="table-designer-constraint-field"
|
|
2010
|
-
data-constraint-id="${escapeHtml(constraint.id)}"
|
|
2011
|
-
data-constraint-kind="unique"
|
|
2012
|
-
data-field="expression"
|
|
2013
|
-
spellcheck="false"
|
|
2014
|
-
>${escapeHtml(renderTableDesignerUniqueConstraintExpression(constraint))}</textarea>
|
|
2015
|
-
</article>
|
|
2016
|
-
`;
|
|
2017
|
-
}
|
|
2018
|
-
|
|
2019
|
-
function renderTableDesignerCheckConstraintEditor(constraint) {
|
|
2020
|
-
const columns = constraint.columns ?? [];
|
|
2021
|
-
const allowedValues = columns.flatMap(column =>
|
|
2022
|
-
(column.allowedValues ?? []).map(value => ({
|
|
2023
|
-
columnName: column.name,
|
|
2024
|
-
value,
|
|
2025
|
-
})),
|
|
2026
|
-
);
|
|
2027
|
-
|
|
2028
|
-
return `
|
|
2029
|
-
<article class="table-designer-constraint-editor">
|
|
2030
|
-
<div class="table-designer-constraint-editor__header">
|
|
2031
|
-
<div class="min-w-0">
|
|
2032
|
-
<div class="table-designer-constraint-editor__type">
|
|
2033
|
-
${columns.length ? escapeHtml(columns.map(column => column.name).join(', ')) : 'Table check'}
|
|
2034
|
-
</div>
|
|
2035
|
-
<input
|
|
2036
|
-
class="table-designer-field table-designer-constraint-editor__name"
|
|
2037
|
-
data-bind="table-designer-constraint-field"
|
|
2038
|
-
data-constraint-id="${escapeHtml(constraint.id)}"
|
|
2039
|
-
data-constraint-kind="check"
|
|
2040
|
-
data-field="name"
|
|
2041
|
-
spellcheck="false"
|
|
2042
|
-
type="text"
|
|
2043
|
-
value="${escapeHtml(constraint.name || 'CHECK constraint')}"
|
|
2044
|
-
/>
|
|
2045
|
-
</div>
|
|
2046
|
-
<div class="status-badge status-badge--muted">Rebuild</div>
|
|
2047
|
-
</div>
|
|
2048
|
-
${
|
|
2049
|
-
allowedValues.length
|
|
2050
|
-
? `
|
|
2051
|
-
<div class="table-designer-constraint__values custom-scrollbar">
|
|
2052
|
-
${allowedValues
|
|
2053
|
-
.map(
|
|
2054
|
-
entry => `
|
|
2055
|
-
<span title="${escapeHtml(entry.columnName)}">${escapeHtml(entry.value)}</span>
|
|
2056
|
-
`,
|
|
2057
|
-
)
|
|
2058
|
-
.join('')}
|
|
2059
|
-
</div>
|
|
2060
|
-
`
|
|
2061
|
-
: ''
|
|
2062
|
-
}
|
|
2063
|
-
<textarea
|
|
2064
|
-
class="table-designer-constraint-editor__sql custom-scrollbar"
|
|
2065
|
-
data-bind="table-designer-constraint-field"
|
|
2066
|
-
data-constraint-id="${escapeHtml(constraint.id)}"
|
|
2067
|
-
data-constraint-kind="check"
|
|
2068
|
-
data-field="expression"
|
|
2069
|
-
spellcheck="false"
|
|
2070
|
-
>${escapeHtml(renderTableDesignerCheckConstraintExpression(constraint))}</textarea>
|
|
2071
|
-
</article>
|
|
2072
|
-
`;
|
|
2073
|
-
}
|
|
2074
|
-
|
|
2075
|
-
function renderTableDesignerConstraintSection({ title, count, emptyText, body }) {
|
|
2076
|
-
return `
|
|
2077
|
-
<section class="table-designer-constraints-modal__section">
|
|
2078
|
-
<div class="table-designer-constraints-modal__section-header">
|
|
2079
|
-
<div class="table-designer-constraints-modal__section-title">${escapeHtml(title)}</div>
|
|
2080
|
-
<div class="status-badge status-badge--muted">${formatNumber(count)}</div>
|
|
2081
|
-
</div>
|
|
2082
|
-
${
|
|
2083
|
-
count
|
|
2084
|
-
? `<div class="table-designer-constraints__list">${body}</div>`
|
|
2085
|
-
: `<div class="table-designer-constraints-modal__empty">${escapeHtml(emptyText)}</div>`
|
|
2086
|
-
}
|
|
2087
|
-
</section>
|
|
2088
|
-
`;
|
|
2089
|
-
}
|
|
2090
|
-
|
|
2091
|
-
function renderTableDesignerConstraintsModal(modal, state) {
|
|
2092
|
-
const draft = state.tableDesigner?.draft;
|
|
2093
|
-
|
|
2094
|
-
if (!draft) {
|
|
2095
|
-
return `
|
|
2096
|
-
<div class="table-designer-constraints-modal__empty">
|
|
2097
|
-
No active table designer draft.
|
|
2098
|
-
</div>
|
|
2099
|
-
<div class="flex items-center justify-between gap-3 pt-2">
|
|
2100
|
-
<button class="standard-button" data-action="close-modal" type="button">Close</button>
|
|
2101
|
-
</div>
|
|
2102
|
-
`;
|
|
2103
|
-
}
|
|
2104
|
-
|
|
2105
|
-
const selectedColumn = modal.columnId ? (draft.columns ?? []).find(column => column.id === modal.columnId) : null;
|
|
2106
|
-
const selectedColumnName = String(modal.columnName ?? selectedColumn?.name ?? '').trim();
|
|
2107
|
-
const hasSelectedColumn = Boolean(modal.columnId || selectedColumnName);
|
|
2108
|
-
const selectedColumnLabel = selectedColumnName || 'Unnamed column';
|
|
2109
|
-
const allUniqueConstraints = draft.mode === 'edit' ? (draft.uniqueConstraints ?? []) : [];
|
|
2110
|
-
const allCheckConstraints = draft.mode === 'edit' ? (draft.checkConstraints ?? []) : [];
|
|
2111
|
-
const uniqueConstraints = hasSelectedColumn
|
|
2112
|
-
? allUniqueConstraints.filter(constraint =>
|
|
2113
|
-
tableDesignerConstraintIncludesColumn(constraint, selectedColumnName),
|
|
2114
|
-
)
|
|
2115
|
-
: allUniqueConstraints;
|
|
2116
|
-
const checkConstraints = hasSelectedColumn
|
|
2117
|
-
? allCheckConstraints.filter(constraint =>
|
|
2118
|
-
tableDesignerConstraintIncludesColumn(constraint, selectedColumnName),
|
|
2119
|
-
)
|
|
2120
|
-
: allCheckConstraints;
|
|
2121
|
-
const totalCount = uniqueConstraints.length + checkConstraints.length;
|
|
2122
|
-
|
|
2123
|
-
return `
|
|
2124
|
-
<div class="table-designer-constraints-modal">
|
|
2125
|
-
<div class="table-designer-constraints-modal__summary">
|
|
2126
|
-
<div class="min-w-0">
|
|
2127
|
-
<div class="table-designer-constraints-modal__label">Table</div>
|
|
2128
|
-
<code title="${escapeHtml(draft.tableName ?? '')}">${escapeHtml(draft.tableName ?? '')}</code>
|
|
2129
|
-
</div>
|
|
2130
|
-
${
|
|
2131
|
-
hasSelectedColumn
|
|
2132
|
-
? `
|
|
2133
|
-
<div class="min-w-0">
|
|
2134
|
-
<div class="table-designer-constraints-modal__label">Column</div>
|
|
2135
|
-
<code title="${escapeHtml(selectedColumnLabel)}">${escapeHtml(selectedColumnLabel)}</code>
|
|
2136
|
-
</div>
|
|
2137
|
-
`
|
|
2138
|
-
: ''
|
|
2139
|
-
}
|
|
2140
|
-
<div class="status-badge status-badge--primary">V2 · ${formatNumber(totalCount)}</div>
|
|
2141
|
-
</div>
|
|
2142
|
-
${renderTableDesignerConstraintSection({
|
|
2143
|
-
title: 'Check constraints',
|
|
2144
|
-
count: checkConstraints.length,
|
|
2145
|
-
emptyText: hasSelectedColumn
|
|
2146
|
-
? 'No check constraints detected for this column.'
|
|
2147
|
-
: 'No check constraints detected.',
|
|
2148
|
-
body: checkConstraints.map(renderTableDesignerCheckConstraintEditor).join(''),
|
|
2149
|
-
})}
|
|
2150
|
-
${renderTableDesignerConstraintSection({
|
|
2151
|
-
title: hasSelectedColumn ? 'Related unique constraints' : 'Unique constraints',
|
|
2152
|
-
count: uniqueConstraints.length,
|
|
2153
|
-
emptyText: hasSelectedColumn
|
|
2154
|
-
? 'No related multi-column or partial unique constraints detected.'
|
|
2155
|
-
: 'No multi-column or partial unique constraints detected.',
|
|
2156
|
-
body: uniqueConstraints.map(renderTableDesignerUniqueConstraintEditor).join(''),
|
|
2157
|
-
})}
|
|
2158
|
-
<div class="flex items-center justify-between gap-3 pt-2">
|
|
2159
|
-
<button class="standard-button" data-action="close-modal" type="button">Close</button>
|
|
2160
|
-
</div>
|
|
2161
|
-
</div>
|
|
2162
|
-
`;
|
|
2163
|
-
}
|
|
2164
|
-
|
|
2165
2042
|
function renderCreateMediaTaggingMappingTableForm(modal, state) {
|
|
2166
2043
|
const mappingExists = hasDefaultMediaTaggingMappingTable(state.mediaTagging.schemaTables ?? []);
|
|
2167
2044
|
const readOnly = Boolean(state.mediaTagging.connection?.readOnly);
|
|
@@ -2346,7 +2223,7 @@ export function renderModal(state) {
|
|
|
2346
2223
|
'edit-connection': {
|
|
2347
2224
|
eyebrow: 'Registry // Update saved SQLite target',
|
|
2348
2225
|
title: 'Edit Connection',
|
|
2349
|
-
body: renderEditConnectionForm(modal),
|
|
2226
|
+
body: renderEditConnectionForm(modal, state),
|
|
2350
2227
|
},
|
|
2351
2228
|
'delete-row': {
|
|
2352
2229
|
eyebrow: 'Mutation // Confirm row deletion',
|
|
@@ -2408,11 +2285,6 @@ export function renderModal(state) {
|
|
|
2408
2285
|
title: isMarkdownTodoCopyColumnMode(modal.copyMode) ? 'Export Markdown Todo' : 'Copy column',
|
|
2409
2286
|
body: renderCopyColumnModal(modal, state),
|
|
2410
2287
|
},
|
|
2411
|
-
'table-designer-constraints': {
|
|
2412
|
-
eyebrow: 'Table Designer // Constraints',
|
|
2413
|
-
title: 'Checks',
|
|
2414
|
-
body: renderTableDesignerConstraintsModal(modal, state),
|
|
2415
|
-
},
|
|
2416
2288
|
'create-media-tagging-tag-table': {
|
|
2417
2289
|
eyebrow: 'Media Tagging // Create default tag table',
|
|
2418
2290
|
title: 'Create Tag Table',
|
|
@@ -2442,8 +2314,7 @@ export function renderModal(state) {
|
|
|
2442
2314
|
modal.kind === 'chart-editor' ||
|
|
2443
2315
|
modal.kind === 'document-insert-table' ||
|
|
2444
2316
|
modal.kind === 'document-insert-note' ||
|
|
2445
|
-
modal.kind === 'row-update-preview'
|
|
2446
|
-
modal.kind === 'table-designer-constraints'
|
|
2317
|
+
modal.kind === 'row-update-preview'
|
|
2447
2318
|
? 'max-w-3xl'
|
|
2448
2319
|
: modal.kind === 'generate-types' || modal.kind === 'generate-data'
|
|
2449
2320
|
? 'max-w-6xl'
|
|
@@ -23,42 +23,14 @@ function canOpenQueryHistoryInCharts(item) {
|
|
|
23
23
|
return Boolean(item?.chartsEligible);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
function getQueryTypeTone(queryType) {
|
|
27
|
-
if (queryType === "select" || queryType === "update") {
|
|
28
|
-
return "success";
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (queryType === "pragma") {
|
|
32
|
-
return "primary";
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return "muted";
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function getExecutionSourceTone(source) {
|
|
39
|
-
if (source === "api") {
|
|
40
|
-
return "primary";
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (source === "cli") {
|
|
44
|
-
return "warning";
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (source === "mcp") {
|
|
48
|
-
return "muted";
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return "success";
|
|
52
|
-
}
|
|
53
|
-
|
|
54
26
|
function renderRunItem(run) {
|
|
55
27
|
const executedBy = String(run.executedBy ?? "user").trim().toLowerCase() || "user";
|
|
56
28
|
|
|
57
29
|
return `
|
|
58
30
|
<div class="border border-outline-variant/10 bg-surface-container px-3 py-3">
|
|
59
31
|
<div class="flex flex-wrap items-center gap-2">
|
|
60
|
-
${renderStatusBadge(run.status,
|
|
61
|
-
${renderStatusBadge(executedBy,
|
|
32
|
+
${renderStatusBadge(run.status, "muted")}
|
|
33
|
+
${renderStatusBadge(executedBy, "muted")}
|
|
62
34
|
<span class="text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
|
|
63
35
|
${escapeHtml(formatCompactDateTime(run.executedAt))}
|
|
64
36
|
</span>
|
|
@@ -100,9 +72,14 @@ export function renderQueryHistoryDetail({
|
|
|
100
72
|
<section class="flex h-full min-h-0 flex-col bg-surface-low">
|
|
101
73
|
<div class="border-b border-outline-variant/10 px-5 py-4">
|
|
102
74
|
<div class="flex items-center justify-between gap-3">
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
75
|
+
<div class="min-w-0">
|
|
76
|
+
<div class="font-mono text-[10px] uppercase tracking-[0.18em] text-primary-container/70">
|
|
77
|
+
SQL Editor // Query History
|
|
78
|
+
</div>
|
|
79
|
+
<h2 class="mt-1 truncate font-body text-lg font-black uppercase tracking-tight text-on-surface">
|
|
80
|
+
Loading
|
|
81
|
+
</h2>
|
|
82
|
+
</div>
|
|
106
83
|
<button
|
|
107
84
|
class="query-history-icon-button"
|
|
108
85
|
data-action="clear-query-history-selection"
|
|
@@ -124,9 +101,14 @@ export function renderQueryHistoryDetail({
|
|
|
124
101
|
<section class="flex h-full min-h-0 flex-col bg-surface-low">
|
|
125
102
|
<div class="border-b border-outline-variant/10 px-5 py-4">
|
|
126
103
|
<div class="flex items-center justify-between gap-3">
|
|
127
|
-
<
|
|
128
|
-
|
|
129
|
-
|
|
104
|
+
<div class="min-w-0">
|
|
105
|
+
<div class="font-mono text-[10px] uppercase tracking-[0.18em] text-primary-container/70">
|
|
106
|
+
SQL Editor // Query History
|
|
107
|
+
</div>
|
|
108
|
+
<h2 class="mt-1 truncate font-body text-lg font-black uppercase tracking-tight text-on-surface">
|
|
109
|
+
Error
|
|
110
|
+
</h2>
|
|
111
|
+
</div>
|
|
130
112
|
<button
|
|
131
113
|
class="query-history-icon-button"
|
|
132
114
|
data-action="clear-query-history-selection"
|
|
@@ -153,12 +135,10 @@ export function renderQueryHistoryDetail({
|
|
|
153
135
|
].join("")
|
|
154
136
|
: "";
|
|
155
137
|
const statusMarkup = [
|
|
156
|
-
renderStatusBadge(item.queryType,
|
|
157
|
-
item.isSaved ? renderStatusBadge("saved", "
|
|
158
|
-
item.isDestructive ? renderStatusBadge("destructive", "
|
|
159
|
-
item.lastRun
|
|
160
|
-
? renderStatusBadge(item.lastRun.status, item.lastRun.status === "error" ? "alert" : "success")
|
|
161
|
-
: "",
|
|
138
|
+
renderStatusBadge(item.queryType, "muted"),
|
|
139
|
+
item.isSaved ? renderStatusBadge("saved", "muted") : "",
|
|
140
|
+
item.isDestructive ? renderStatusBadge("destructive", "muted") : "",
|
|
141
|
+
item.lastRun ? renderStatusBadge(item.lastRun.status, "muted") : "",
|
|
162
142
|
].join("");
|
|
163
143
|
const metaMarkup = [
|
|
164
144
|
renderDetailMetaItem("Last Used", formatDateTime(item.lastUsedAt)),
|
|
@@ -176,9 +156,9 @@ export function renderQueryHistoryDetail({
|
|
|
176
156
|
return [
|
|
177
157
|
'<section class="flex h-full min-h-0 flex-col bg-surface-low">',
|
|
178
158
|
'<div class="border-b border-outline-variant/10 px-5 py-4">',
|
|
179
|
-
'<div class="flex items-center justify-between gap-3"><div>',
|
|
180
|
-
'<div class="text-[10px]
|
|
181
|
-
'<h2 class="mt-1 font-body text-lg font-black uppercase tracking-tight text-on-surface">',
|
|
159
|
+
'<div class="flex items-center justify-between gap-3"><div class="min-w-0">',
|
|
160
|
+
'<div class="font-mono text-[10px] uppercase tracking-[0.18em] text-primary-container/70">SQL Editor // Query History</div>',
|
|
161
|
+
'<h2 class="mt-1 truncate font-body text-lg font-black uppercase tracking-tight text-on-surface">',
|
|
182
162
|
escapeHtml(item.displayTitle),
|
|
183
163
|
"</h2></div>",
|
|
184
164
|
'<button class="query-history-icon-button" data-action="clear-query-history-selection" type="button"><span class="material-symbols-outlined text-[18px]">close</span></button>',
|
|
@@ -215,7 +215,7 @@ function renderTextCharacterCountBadge(value) {
|
|
|
215
215
|
|
|
216
216
|
return `
|
|
217
217
|
<span
|
|
218
|
-
class="
|
|
218
|
+
class="status-badge status-badge--muted"
|
|
219
219
|
data-row-editor-char-count
|
|
220
220
|
${count === null ? "hidden" : ""}
|
|
221
221
|
>
|
|
@@ -337,22 +337,10 @@ function renderReadonlyField(field = {}, tableMeta = {}) {
|
|
|
337
337
|
`;
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
-
function getValueStateBadgeClassName(state) {
|
|
341
|
-
if (state === "null") {
|
|
342
|
-
return "border-primary-container/35 bg-primary-container/15 text-primary-container";
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
if (state === "empty") {
|
|
346
|
-
return "border-outline-variant/35 bg-surface-container-high text-on-surface-variant";
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
return "border-outline-variant/20 bg-surface-container text-on-surface-variant";
|
|
350
|
-
}
|
|
351
|
-
|
|
352
340
|
function renderValueStateBadge(state) {
|
|
353
341
|
return `
|
|
354
342
|
<span
|
|
355
|
-
class="
|
|
343
|
+
class="status-badge status-badge--muted"
|
|
356
344
|
data-row-editor-value-state
|
|
357
345
|
data-value-state="${escapeHtml(state)}"
|
|
358
346
|
>${escapeHtml(getRowEditorValueStateLabel(state))}</span>
|
|
@@ -447,44 +435,11 @@ function renderEditableField(field, tableMeta = {}) {
|
|
|
447
435
|
`;
|
|
448
436
|
}
|
|
449
437
|
|
|
450
|
-
function getFieldBadgeClassName(tone) {
|
|
451
|
-
if (tone === "primary-key") {
|
|
452
|
-
return "border-primary-container/35 bg-primary-container/15 text-primary-container";
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
if (tone === "foreign-key") {
|
|
456
|
-
return "border-tertiary-fixed-dim/35 bg-tertiary-fixed-dim/15 text-tertiary-fixed-dim";
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
if (tone === "url") {
|
|
460
|
-
return "border-primary-container/35 bg-primary-container/15 text-primary-container";
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
if (tone === "email") {
|
|
464
|
-
return "border-tertiary-fixed-dim/35 bg-tertiary-fixed-dim/15 text-tertiary-fixed-dim";
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
if (tone === "check") {
|
|
468
|
-
return "border-tertiary-fixed-dim/35 bg-tertiary-fixed-dim/15 text-tertiary-fixed-dim";
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
if (tone === "filepath") {
|
|
472
|
-
return "border-outline-variant/30 bg-surface-container-high text-on-surface";
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
if (tone === "char-count") {
|
|
476
|
-
return "border-outline-variant/25 bg-surface-container text-on-surface-variant";
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
return "border-outline-variant/20 bg-surface-container text-on-surface-variant";
|
|
480
|
-
}
|
|
481
|
-
|
|
482
438
|
function renderFieldBadge(badge) {
|
|
483
439
|
const label = typeof badge === "object" ? badge.label : badge;
|
|
484
|
-
const tone = typeof badge === "object" ? badge.tone : "";
|
|
485
440
|
|
|
486
441
|
return `
|
|
487
|
-
<span class="
|
|
442
|
+
<span class="status-badge status-badge--muted">
|
|
488
443
|
${escapeHtml(label)}
|
|
489
444
|
</span>
|
|
490
445
|
`;
|
|
@@ -578,27 +533,19 @@ export function renderRowEditorPanel({
|
|
|
578
533
|
]
|
|
579
534
|
.filter(Boolean)
|
|
580
535
|
.join("");
|
|
536
|
+
const eyebrow = [sectionLabel, subtitle].filter(Boolean).join(" // ");
|
|
581
537
|
|
|
582
538
|
return `
|
|
583
539
|
<section class="flex h-full min-h-0 flex-col bg-surface-low">
|
|
584
540
|
<header class="border-b border-outline-variant/10 bg-surface-container px-6 py-5">
|
|
585
541
|
<div class="flex items-start justify-between gap-4">
|
|
586
542
|
<div class="min-w-0 flex-1">
|
|
587
|
-
<div class="text-[10px]
|
|
588
|
-
${escapeHtml(
|
|
543
|
+
<div class="font-mono text-[10px] uppercase tracking-[0.18em] text-primary-container/70">
|
|
544
|
+
${escapeHtml(eyebrow)}
|
|
589
545
|
</div>
|
|
590
|
-
<h2 class="mt-
|
|
546
|
+
<h2 class="mt-1 truncate font-body text-lg font-black uppercase tracking-tight text-on-surface">
|
|
591
547
|
${escapeHtml(title)}
|
|
592
548
|
</h2>
|
|
593
|
-
${
|
|
594
|
-
subtitle
|
|
595
|
-
? `
|
|
596
|
-
<div class="mt-2 text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
|
|
597
|
-
${escapeHtml(subtitle)}
|
|
598
|
-
</div>
|
|
599
|
-
`
|
|
600
|
-
: ""
|
|
601
|
-
}
|
|
602
549
|
</div>
|
|
603
550
|
<button
|
|
604
551
|
aria-label="Close panel"
|