sqlite-hub 1.1.2 → 1.4.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 +106 -73
- package/bin/sqlite-hub.js +176 -2
- package/docs/API.md +122 -0
- package/docs/CLI.md +227 -0
- package/docs/DESIGN_GUIDELINES.md +45 -0
- package/docs/changelog.md +141 -0
- package/docs/shortkeys.md +27 -0
- package/docs/todo.md +16 -0
- package/examples/api/generate-types.js +52 -0
- package/frontend/assets/mockups/backups_1_1920.webp +0 -0
- 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/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_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/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 +18 -0
- package/frontend/js/app.js +209 -13
- package/frontend/js/components/connectionCard.js +1 -1
- package/frontend/js/components/emptyState.js +4 -4
- package/frontend/js/components/generateTypesDropdown.js +33 -0
- package/frontend/js/components/metricCard.js +1 -1
- package/frontend/js/components/modal.js +211 -18
- package/frontend/js/components/pageHeader.js +1 -1
- package/frontend/js/components/queryHistoryDetail.js +3 -3
- package/frontend/js/components/queryHistoryHeader.js +1 -1
- package/frontend/js/components/queryHistoryPanel.js +1 -1
- package/frontend/js/components/queryResults.js +1 -1
- package/frontend/js/components/rowEditorPanel.js +65 -16
- package/frontend/js/components/topNav.js +1 -4
- package/frontend/js/components/workspaceOpenDropdown.js +52 -0
- package/frontend/js/router.js +28 -6
- package/frontend/js/store.js +489 -9
- package/frontend/js/utils/emailPreview.js +28 -0
- package/frontend/js/utils/markdownDocuments.js +17 -1
- package/frontend/js/views/backups.js +556 -36
- package/frontend/js/views/charts.js +9 -9
- package/frontend/js/views/connections.js +2 -2
- package/frontend/js/views/data.js +43 -17
- package/frontend/js/views/documents.js +3 -3
- package/frontend/js/views/editor.js +6 -6
- package/frontend/js/views/mediaTagging.js +7 -5
- package/frontend/js/views/overview.js +3 -3
- package/frontend/js/views/settings.js +4 -3
- package/frontend/js/views/structure.js +27 -10
- package/frontend/js/views/tableDesigner.js +23 -0
- package/frontend/styles/base.css +1 -40
- package/frontend/styles/components.css +114 -232
- package/frontend/styles/structure-graph.css +32 -68
- package/frontend/styles/tailwind.css +8 -4
- package/frontend/styles/tailwind.generated.css +1 -1
- package/frontend/styles/tokens.css +29 -21
- package/frontend/styles/views.css +94 -261
- package/package.json +18 -2
- package/server/routes/backups.js +18 -2
- package/server/routes/externalApi.js +36 -0
- package/server/routes/structure.js +22 -0
- package/server/services/databaseCommandService.js +14 -0
- package/server/services/sqlite/backupDiff.js +914 -0
- package/server/services/sqlite/backupService.js +77 -1
- package/server/services/sqlite/structureService.js +7 -0
- package/server/services/typeGenerationService.js +663 -0
- package/tailwind.config.cjs +0 -1
- package/.github/funding.yml +0 -2
- package/.github/workflows/ci.yml +0 -36
- package/database.sqlite +0 -0
- package/frontend/assets/mockups/charts_1_bars_1200.webp +0 -0
- package/frontend/assets/mockups/charts_2_pie_1200.webp +0 -0
- package/frontend/assets/mockups/charts_3_scatter_plot_1200.webp +0 -0
- package/frontend/assets/mockups/connections_1200.webp +0 -0
- package/frontend/assets/mockups/data_1_1200.webp +0 -0
- package/frontend/assets/mockups/data_2_row_editor_1200.webp +0 -0
- package/frontend/assets/mockups/documents_1200.webp +0 -0
- package/frontend/assets/mockups/media_tagging_1_setup_1200.webp +0 -0
- package/frontend/assets/mockups/media_tagging_2_tagging_queue_1200.webp +0 -0
- package/frontend/assets/mockups/media_tagging_3_media_viewer_1200.webp +0 -0
- package/frontend/assets/mockups/overview_1200.webp +0 -0
- package/frontend/assets/mockups/settings_1200.webp +0 -0
- package/frontend/assets/mockups/sql_editor_1_1200.webp +0 -0
- package/frontend/assets/mockups/sql_editor_2_query_details_1200.webp +0 -0
- package/frontend/assets/mockups/sql_editor_3_export_column_1200.webp +0 -0
- package/frontend/assets/mockups/sql_editor_4_export_column_as_markdown_1200.webp +0 -0
- package/frontend/assets/mockups/sql_editor_5_export_query_result_1200.webp +0 -0
- package/frontend/assets/mockups/structure_1_1200.webp +0 -0
- package/frontend/assets/mockups/structure_2_inspector_1200.webp +0 -0
- package/frontend/assets/mockups/table_designer_1_1200.webp +0 -0
- package/frontend/assets/mockups/table_designer_2_checks_1200.webp +0 -0
- package/tests/api-token-auth.test.js +0 -279
- package/tests/backup-manager.test.js +0 -140
- package/tests/backups-view.test.js +0 -64
- package/tests/charts-height-preset-storage.test.js +0 -60
- package/tests/charts-route-state.test.js +0 -144
- package/tests/check-constraint-options.test.js +0 -90
- package/tests/cli-args.test.js +0 -113
- package/tests/cli-service-delegation.test.js +0 -140
- package/tests/connection-removal.test.js +0 -52
- package/tests/connections-file-dialog-route.test.js +0 -89
- package/tests/copy-column-modal.test.js +0 -131
- package/tests/database-command-service.test.js +0 -174
- package/tests/database-documents.test.js +0 -85
- package/tests/documents-view.test.js +0 -132
- package/tests/dropdown-button.test.js +0 -75
- package/tests/export-blob.test.js +0 -99
- package/tests/export-filenames.test.js +0 -38
- package/tests/file-path-preview.test.js +0 -165
- package/tests/form-controls.test.js +0 -34
- package/tests/json-preview.test.js +0 -49
- package/tests/local-request-security.test.js +0 -85
- package/tests/markdown-documents.test.js +0 -79
- package/tests/native-file-dialog.test.js +0 -105
- package/tests/query-editor.test.js +0 -28
- package/tests/query-history-detail.test.js +0 -37
- package/tests/query-history-header.test.js +0 -30
- package/tests/query-results-truncation.test.js +0 -20
- package/tests/risky-sql.test.js +0 -30
- package/tests/row-editor-json.test.js +0 -82
- package/tests/row-editor-null-values.test.js +0 -155
- package/tests/row-editor-timestamp-preview.test.js +0 -192
- package/tests/security-paths.test.js +0 -84
- package/tests/settings-api-tokens-route.test.js +0 -97
- package/tests/settings-metadata.test.js +0 -114
- package/tests/settings-view.test.js +0 -107
- package/tests/sql-formatter.test.js +0 -173
- package/tests/sql-highlight.test.js +0 -38
- package/tests/sql-identifier-safety.test.js +0 -171
- package/tests/sql-result-limit.test.js +0 -66
- package/tests/structure-view.test.js +0 -56
- package/tests/table-designer-v2-unique-constraints.test.js +0 -78
- package/tests/table-scroll-state.test.js +0 -70
- package/tests/text-cell-stats.test.js +0 -38
|
Binary file
|
package/frontend/assets/mockups/media_tagging_setup_2_create_media_tagging_tag_table_modal_1920.webp
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/frontend/js/api.js
CHANGED
|
@@ -153,6 +153,17 @@ export function getBackup(backupId) {
|
|
|
153
153
|
return request(`/api/backups/${encodeURIComponent(backupId)}`);
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
export function getBackupDiff(backupId, options = {}) {
|
|
157
|
+
const params = new URLSearchParams();
|
|
158
|
+
|
|
159
|
+
if (options.sampleLimit !== undefined) {
|
|
160
|
+
params.set("sampleLimit", String(options.sampleLimit));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const query = params.toString();
|
|
164
|
+
return request(`/api/backups/${encodeURIComponent(backupId)}/diff${query ? `?${query}` : ""}`);
|
|
165
|
+
}
|
|
166
|
+
|
|
156
167
|
export function createBackup(payload = {}) {
|
|
157
168
|
return request("/api/backups", {
|
|
158
169
|
method: "POST",
|
|
@@ -340,6 +351,13 @@ export function getStructureDetail(tableName) {
|
|
|
340
351
|
return request(`/api/structure/${encodeURIComponent(tableName)}`);
|
|
341
352
|
}
|
|
342
353
|
|
|
354
|
+
export function generateStructureTypes(tableName, payload = {}) {
|
|
355
|
+
return request(`/api/structure/${encodeURIComponent(tableName)}/types`, {
|
|
356
|
+
method: "POST",
|
|
357
|
+
body: payload,
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
343
361
|
export function getTableDesignerOverview() {
|
|
344
362
|
return request("/api/table-designer");
|
|
345
363
|
}
|
package/frontend/js/app.js
CHANGED
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
exportCurrentQueryFormat,
|
|
47
47
|
getState,
|
|
48
48
|
initializeApp,
|
|
49
|
+
insertMarkdownIntoLastOpenDocument,
|
|
49
50
|
loadMoreQueryHistory,
|
|
50
51
|
openModal,
|
|
51
52
|
openOverviewInFinder,
|
|
@@ -55,7 +56,10 @@ import {
|
|
|
55
56
|
openDeleteQueryHistoryModal,
|
|
56
57
|
openDeleteSettingsApiTokenModal,
|
|
57
58
|
openCreateBackupModal,
|
|
58
|
-
|
|
59
|
+
openEditBackupModal,
|
|
60
|
+
openBackupDiffDrawer,
|
|
61
|
+
closeBackupDiffDrawer,
|
|
62
|
+
openGenerateTypesModal,
|
|
59
63
|
openDeleteBackupModal,
|
|
60
64
|
openRestoreBackupModal,
|
|
61
65
|
openDeleteQueryChartModal,
|
|
@@ -121,7 +125,7 @@ import {
|
|
|
121
125
|
submitDeleteSettingsApiTokenConfirmation,
|
|
122
126
|
submitBackupSafetyChoice,
|
|
123
127
|
submitCreateBackupConfirmation,
|
|
124
|
-
|
|
128
|
+
submitEditBackupConfirmation,
|
|
125
129
|
submitRowUpdatePreviewConfirmation,
|
|
126
130
|
setQueryHistoryPanelVisibility,
|
|
127
131
|
sortDataTableByColumn,
|
|
@@ -131,6 +135,7 @@ import {
|
|
|
131
135
|
setCopyColumnModalError,
|
|
132
136
|
setCopyColumnModalEditedText,
|
|
133
137
|
setCopyColumnModalSubmitting,
|
|
138
|
+
setBackupDiffTab,
|
|
134
139
|
setRoute,
|
|
135
140
|
setSettingsSection,
|
|
136
141
|
saveQueryHistoryNotes,
|
|
@@ -165,6 +170,7 @@ import {
|
|
|
165
170
|
updateCurrentTableDesignerColumnField,
|
|
166
171
|
updateCurrentTableDesignerConstraintField,
|
|
167
172
|
updateCurrentTableDesignerField,
|
|
173
|
+
updateGenerateTypesModal,
|
|
168
174
|
addCurrentTableDesignerColumn,
|
|
169
175
|
applyCurrentMediaTaggingSelection,
|
|
170
176
|
removeCurrentTableDesignerColumn,
|
|
@@ -239,6 +245,7 @@ let lastRenderedPanelMarkup = '';
|
|
|
239
245
|
let lastRenderedModalMarkup = '';
|
|
240
246
|
let lastRenderedToastMarkup = '';
|
|
241
247
|
let lastRenderedChartsHistorySignature = '';
|
|
248
|
+
let lastRenderedChartsDetailSignature = '';
|
|
242
249
|
let lastRenderedChartsCardSignature = '';
|
|
243
250
|
let lastRenderedPanelOpen = false;
|
|
244
251
|
let lastRenderedLockedRoute = false;
|
|
@@ -786,6 +793,27 @@ function buildChartsCardSignature(state) {
|
|
|
786
793
|
});
|
|
787
794
|
}
|
|
788
795
|
|
|
796
|
+
function buildChartsDetailSignature(state) {
|
|
797
|
+
if (state.route.name !== 'charts') {
|
|
798
|
+
return '';
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
const detail = state.charts.detail;
|
|
802
|
+
const historyVisible = state.charts.historyPanelVisible !== false || !state.charts.selectedHistoryId;
|
|
803
|
+
|
|
804
|
+
return JSON.stringify({
|
|
805
|
+
selectedHistoryId: state.charts.selectedHistoryId ?? null,
|
|
806
|
+
historyVisible,
|
|
807
|
+
detailLoading: Boolean(state.charts.detailLoading),
|
|
808
|
+
detailError: state.charts.detailError
|
|
809
|
+
? [state.charts.detailError.code ?? '', state.charts.detailError.message ?? '']
|
|
810
|
+
: null,
|
|
811
|
+
hasDetailItem: Boolean(detail?.item),
|
|
812
|
+
displayTitle: detail?.item?.displayTitle ?? '',
|
|
813
|
+
cards: buildChartsCardSignature(state),
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
|
|
789
817
|
function syncChartsHistorySelectionUi(state) {
|
|
790
818
|
const selectedHistoryId = String(state.charts.selectedHistoryId ?? '');
|
|
791
819
|
const historyButtons = shellRefs.view.querySelectorAll(
|
|
@@ -1042,11 +1070,11 @@ function renderNotFoundView() {
|
|
|
1042
1070
|
<p class="font-mono text-[10px] uppercase tracking-[0.3em] text-primary-container/40">
|
|
1043
1071
|
ROUTE_LOST // HASH_NOT_RECOGNIZED
|
|
1044
1072
|
</p>
|
|
1045
|
-
<h1 class="mt-4 font-
|
|
1073
|
+
<h1 class="mt-4 font-body text-6xl font-black uppercase tracking-tight text-primary-container">
|
|
1046
1074
|
404_SIGNAL
|
|
1047
1075
|
</h1>
|
|
1048
1076
|
<button
|
|
1049
|
-
class="standard-button mt-8 px-6 font-
|
|
1077
|
+
class="standard-button mt-8 px-6 font-body text-sm"
|
|
1050
1078
|
data-action="navigate"
|
|
1051
1079
|
data-to="/"
|
|
1052
1080
|
type="button"
|
|
@@ -1368,6 +1396,7 @@ function renderApp(state) {
|
|
|
1368
1396
|
const modalMarkup = renderModal(state);
|
|
1369
1397
|
const toastMarkup = renderToasts(state.toasts);
|
|
1370
1398
|
const chartsHistorySignature = buildChartsHistorySignature(state);
|
|
1399
|
+
const chartsDetailSignature = buildChartsDetailSignature(state);
|
|
1371
1400
|
const chartsCardSignature = buildChartsCardSignature(state);
|
|
1372
1401
|
const isLockedRoute = [
|
|
1373
1402
|
'editor',
|
|
@@ -1388,6 +1417,7 @@ function renderApp(state) {
|
|
|
1388
1417
|
const panelChanged = panel !== lastRenderedPanelMarkup;
|
|
1389
1418
|
const modalChanged = modalMarkup !== lastRenderedModalMarkup;
|
|
1390
1419
|
const chartsHistoryChanged = chartsHistorySignature !== lastRenderedChartsHistorySignature;
|
|
1420
|
+
const chartsDetailChanged = chartsDetailSignature !== lastRenderedChartsDetailSignature;
|
|
1391
1421
|
const chartsCardsChanged = chartsCardSignature !== lastRenderedChartsCardSignature;
|
|
1392
1422
|
const panelOpenChanged = panelOpen !== lastRenderedPanelOpen;
|
|
1393
1423
|
const lockedRouteChanged = isLockedRoute !== lastRenderedLockedRoute;
|
|
@@ -1436,12 +1466,17 @@ function renderApp(state) {
|
|
|
1436
1466
|
const historyPatched = !chartsHistoryChanged || patchChartsHistoryUi(state);
|
|
1437
1467
|
|
|
1438
1468
|
if (historyPatched) {
|
|
1439
|
-
if (
|
|
1440
|
-
|
|
1441
|
-
|
|
1469
|
+
if (chartsDetailChanged) {
|
|
1470
|
+
if (chartsCardsChanged) {
|
|
1471
|
+
teardownQueryChartRenderer();
|
|
1472
|
+
}
|
|
1442
1473
|
|
|
1443
|
-
|
|
1444
|
-
|
|
1474
|
+
preservedChartsDom = !chartsCardsChanged;
|
|
1475
|
+
mainPatched = patchChartsDetailUi(state, { preserveCharts: preservedChartsDom });
|
|
1476
|
+
} else {
|
|
1477
|
+
preservedChartsDom = true;
|
|
1478
|
+
mainPatched = syncChartsHistorySelectionUi(state);
|
|
1479
|
+
}
|
|
1445
1480
|
}
|
|
1446
1481
|
|
|
1447
1482
|
if (!mainPatched) {
|
|
@@ -1542,6 +1577,7 @@ function renderApp(state) {
|
|
|
1542
1577
|
lastRenderedModalMarkup = modalMarkup;
|
|
1543
1578
|
lastRenderedToastMarkup = toastMarkup;
|
|
1544
1579
|
lastRenderedChartsHistorySignature = chartsHistorySignature;
|
|
1580
|
+
lastRenderedChartsDetailSignature = chartsDetailSignature;
|
|
1545
1581
|
lastRenderedChartsCardSignature = chartsCardSignature;
|
|
1546
1582
|
lastRenderedPanelOpen = panelOpen;
|
|
1547
1583
|
lastRenderedLockedRoute = isLockedRoute;
|
|
@@ -1567,6 +1603,22 @@ async function executeEditorQueryAndNavigate() {
|
|
|
1567
1603
|
router.navigate(success && activeTab === 'results' ? '/editor/results' : '/editor');
|
|
1568
1604
|
}
|
|
1569
1605
|
|
|
1606
|
+
function quoteSqlIdentifier(identifier) {
|
|
1607
|
+
return `"${String(identifier ?? '').replace(/"/g, '""')}"`;
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
async function openTableInSqlEditor(tableName) {
|
|
1611
|
+
const normalizedTableName = String(tableName ?? '').trim();
|
|
1612
|
+
|
|
1613
|
+
if (!normalizedTableName) {
|
|
1614
|
+
showToast('No table selected for SQL Editor.', 'alert');
|
|
1615
|
+
return;
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
setCurrentQuery(`SELECT * FROM ${quoteSqlIdentifier(normalizedTableName)};`);
|
|
1619
|
+
router.navigate('/editor');
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1570
1622
|
function formatCurrentQuery() {
|
|
1571
1623
|
const currentQuery = getState().editor.sqlText ?? '';
|
|
1572
1624
|
const formattedQuery = formatSqlQuery(currentQuery);
|
|
@@ -1956,6 +2008,68 @@ function downloadTextFile({ text, filename, mimeType }) {
|
|
|
1956
2008
|
URL.revokeObjectURL(url);
|
|
1957
2009
|
}
|
|
1958
2010
|
|
|
2011
|
+
async function copyGeneratedTypes() {
|
|
2012
|
+
const modal = getState().modal;
|
|
2013
|
+
const code = modal?.kind === 'generate-types' ? String(modal.result?.code ?? '') : '';
|
|
2014
|
+
|
|
2015
|
+
if (!code) {
|
|
2016
|
+
showToast('No generated code to copy.', 'alert');
|
|
2017
|
+
return;
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
if (!navigator.clipboard?.writeText) {
|
|
2021
|
+
showToast('Clipboard API is not available.', 'alert');
|
|
2022
|
+
return;
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
try {
|
|
2026
|
+
await navigator.clipboard.writeText(code);
|
|
2027
|
+
showToast(
|
|
2028
|
+
modal.result?.files?.length ? 'Type definitions copied to clipboard.' : 'Type definition copied to clipboard.',
|
|
2029
|
+
'success',
|
|
2030
|
+
);
|
|
2031
|
+
} catch (error) {
|
|
2032
|
+
showToast(
|
|
2033
|
+
modal.result?.files?.length
|
|
2034
|
+
? 'Type definitions could not be copied.'
|
|
2035
|
+
: 'Type definition could not be copied.',
|
|
2036
|
+
'alert',
|
|
2037
|
+
);
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
function downloadGeneratedTypes() {
|
|
2042
|
+
const modal = getState().modal;
|
|
2043
|
+
const result = modal?.kind === 'generate-types' ? modal.result : null;
|
|
2044
|
+
const files = result?.files ?? [];
|
|
2045
|
+
|
|
2046
|
+
if (files.length) {
|
|
2047
|
+
files.forEach((file, index) => {
|
|
2048
|
+
window.setTimeout(() => {
|
|
2049
|
+
downloadTextFile({
|
|
2050
|
+
text: file.code,
|
|
2051
|
+
filename: file.fileName || `${file.tableName || 'types'}.txt`,
|
|
2052
|
+
mimeType: 'text/plain;charset=utf-8',
|
|
2053
|
+
});
|
|
2054
|
+
}, index * 120);
|
|
2055
|
+
});
|
|
2056
|
+
showToast(`${files.length} type definition downloads started.`, 'success');
|
|
2057
|
+
return;
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
if (!result?.code) {
|
|
2061
|
+
showToast('No generated code to download.', 'alert');
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
downloadTextFile({
|
|
2066
|
+
text: result.code,
|
|
2067
|
+
filename: result.fileName || 'types.txt',
|
|
2068
|
+
mimeType: 'text/plain;charset=utf-8',
|
|
2069
|
+
});
|
|
2070
|
+
showToast('Type definition download started.', 'success');
|
|
2071
|
+
}
|
|
2072
|
+
|
|
1959
2073
|
function getSelectedDataBrowserRowForJson(state) {
|
|
1960
2074
|
if (state.dataBrowser.selectedRow) {
|
|
1961
2075
|
return state.dataBrowser.selectedRow;
|
|
@@ -2103,6 +2217,39 @@ function exportRowEditorJson() {
|
|
|
2103
2217
|
showToast(`Row from "${payload.label}" exported as JSON.`, 'success');
|
|
2104
2218
|
}
|
|
2105
2219
|
|
|
2220
|
+
async function insertRowEditorJsonIntoDocument() {
|
|
2221
|
+
const payload = buildRowEditorJsonPayload(getState());
|
|
2222
|
+
|
|
2223
|
+
if (!payload) {
|
|
2224
|
+
showToast('No row is selected.', 'alert');
|
|
2225
|
+
return;
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
const markdown = ['```json', payload.text, '```'].join('\n');
|
|
2229
|
+
|
|
2230
|
+
try {
|
|
2231
|
+
const result = await insertMarkdownIntoLastOpenDocument(markdown);
|
|
2232
|
+
|
|
2233
|
+
if (!result.documentId) {
|
|
2234
|
+
showToast('Open a document before inserting row JSON.', 'alert');
|
|
2235
|
+
return;
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
if (!result.inserted) {
|
|
2239
|
+
showToast('Row JSON could not be inserted.', 'alert');
|
|
2240
|
+
return;
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
if (!result.saved) {
|
|
2244
|
+
scheduleDocumentAutosave(result.documentId);
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
showToast(`Row from "${payload.label}" inserted into document.`, 'success');
|
|
2248
|
+
} catch (error) {
|
|
2249
|
+
showToast('Open a document before inserting row JSON.', 'alert');
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2106
2253
|
function exportCurrentDocumentMarkdown() {
|
|
2107
2254
|
const documents = getState().documents;
|
|
2108
2255
|
|
|
@@ -2319,6 +2466,9 @@ async function handleAction(actionNode) {
|
|
|
2319
2466
|
case 'export-row-editor-json':
|
|
2320
2467
|
exportRowEditorJson();
|
|
2321
2468
|
return;
|
|
2469
|
+
case 'insert-row-editor-json-into-document':
|
|
2470
|
+
await insertRowEditorJsonIntoDocument();
|
|
2471
|
+
return;
|
|
2322
2472
|
case 'open-modal':
|
|
2323
2473
|
openModal(actionNode.dataset.modal, {
|
|
2324
2474
|
columnId: actionNode.dataset.columnId,
|
|
@@ -2496,11 +2646,32 @@ async function handleAction(actionNode) {
|
|
|
2496
2646
|
case 'refresh-backups':
|
|
2497
2647
|
await refreshBackups();
|
|
2498
2648
|
return;
|
|
2649
|
+
case 'open-table-in-sql-editor':
|
|
2650
|
+
await openTableInSqlEditor(actionNode.dataset.tableName);
|
|
2651
|
+
return;
|
|
2652
|
+
case 'open-generate-types-modal':
|
|
2653
|
+
await openGenerateTypesModal(actionNode.dataset.tableName, actionNode.dataset.typeTarget, actionNode.dataset.typeScope);
|
|
2654
|
+
return;
|
|
2655
|
+
case 'copy-generated-types':
|
|
2656
|
+
await copyGeneratedTypes();
|
|
2657
|
+
return;
|
|
2658
|
+
case 'download-generated-types':
|
|
2659
|
+
downloadGeneratedTypes();
|
|
2660
|
+
return;
|
|
2499
2661
|
case 'open-restore-backup-modal':
|
|
2500
2662
|
openRestoreBackupModal(actionNode.dataset.backupId);
|
|
2501
2663
|
return;
|
|
2502
|
-
case 'open-
|
|
2503
|
-
|
|
2664
|
+
case 'open-compare-backup-drawer':
|
|
2665
|
+
await openBackupDiffDrawer(actionNode.dataset.backupId);
|
|
2666
|
+
return;
|
|
2667
|
+
case 'set-backup-diff-tab':
|
|
2668
|
+
setBackupDiffTab(actionNode.dataset.tab);
|
|
2669
|
+
return;
|
|
2670
|
+
case 'close-backup-diff-drawer':
|
|
2671
|
+
closeBackupDiffDrawer();
|
|
2672
|
+
return;
|
|
2673
|
+
case 'open-edit-backup-modal':
|
|
2674
|
+
openEditBackupModal(actionNode.dataset.backupId);
|
|
2504
2675
|
return;
|
|
2505
2676
|
case 'open-delete-backup-modal':
|
|
2506
2677
|
openDeleteBackupModal(actionNode.dataset.backupId);
|
|
@@ -3095,6 +3266,12 @@ document.addEventListener('keydown', event => {
|
|
|
3095
3266
|
return;
|
|
3096
3267
|
}
|
|
3097
3268
|
|
|
3269
|
+
if (state.route.name === 'backups' && state.backups.diff?.visible) {
|
|
3270
|
+
event.preventDefault();
|
|
3271
|
+
closeBackupDiffDrawer();
|
|
3272
|
+
return;
|
|
3273
|
+
}
|
|
3274
|
+
|
|
3098
3275
|
if (state.editor.historySelectedId !== null || state.editor.historyDetail) {
|
|
3099
3276
|
event.preventDefault();
|
|
3100
3277
|
clearQueryHistorySelection();
|
|
@@ -3141,6 +3318,15 @@ document.addEventListener('input', event => {
|
|
|
3141
3318
|
return;
|
|
3142
3319
|
}
|
|
3143
3320
|
|
|
3321
|
+
if (bindNode.dataset.bind === 'type-generation-field') {
|
|
3322
|
+
if (bindNode instanceof HTMLInputElement && bindNode.type === 'checkbox') {
|
|
3323
|
+
return;
|
|
3324
|
+
}
|
|
3325
|
+
|
|
3326
|
+
void updateGenerateTypesModal(bindNode.dataset.typeGenerationField, bindNode.value);
|
|
3327
|
+
return;
|
|
3328
|
+
}
|
|
3329
|
+
|
|
3144
3330
|
if (bindNode.dataset.bind === 'copy-column-format-field') {
|
|
3145
3331
|
updateCopyColumnModalFormatField(
|
|
3146
3332
|
bindNode.dataset.field,
|
|
@@ -3312,6 +3498,13 @@ document.addEventListener('change', event => {
|
|
|
3312
3498
|
return;
|
|
3313
3499
|
}
|
|
3314
3500
|
|
|
3501
|
+
if (bindNode.dataset.bind === 'type-generation-field') {
|
|
3502
|
+
const nextValue =
|
|
3503
|
+
bindNode instanceof HTMLInputElement && bindNode.type === 'checkbox' ? bindNode.checked : bindNode.value;
|
|
3504
|
+
void updateGenerateTypesModal(bindNode.dataset.typeGenerationField, nextValue);
|
|
3505
|
+
return;
|
|
3506
|
+
}
|
|
3507
|
+
|
|
3315
3508
|
if (bindNode.dataset.bind === 'data-search-column') {
|
|
3316
3509
|
void setDataSearchColumn(bindNode.value);
|
|
3317
3510
|
return;
|
|
@@ -3537,8 +3730,11 @@ document.addEventListener('submit', async event => {
|
|
|
3537
3730
|
type: String(formData.get('type') ?? 'manual'),
|
|
3538
3731
|
});
|
|
3539
3732
|
return;
|
|
3540
|
-
case 'edit-backup
|
|
3541
|
-
await
|
|
3733
|
+
case 'edit-backup':
|
|
3734
|
+
await submitEditBackupConfirmation({
|
|
3735
|
+
name: String(formData.get('name') ?? ''),
|
|
3736
|
+
notes: String(formData.get('notes') ?? ''),
|
|
3737
|
+
});
|
|
3542
3738
|
return;
|
|
3543
3739
|
case 'delete-backup-confirm':
|
|
3544
3740
|
await submitDeleteBackupConfirmation();
|
|
@@ -36,7 +36,7 @@ export function renderConnectionCard(connection, activeConnectionId) {
|
|
|
36
36
|
renderStatusBadge(isActive ? "ACTIVE" : "RECENT", isActive ? "primary" : "muted"),
|
|
37
37
|
connection.readOnly ? renderStatusBadge("READ_ONLY", "alert") : "",
|
|
38
38
|
"</div></div>",
|
|
39
|
-
'<h3 class="mb-1 font-
|
|
39
|
+
'<h3 class="mb-1 font-body text-xl font-bold uppercase ',
|
|
40
40
|
isActive ? "text-[#FCE300]" : "text-on-surface",
|
|
41
41
|
'">',
|
|
42
42
|
escapeHtml(connection.label),
|
|
@@ -61,7 +61,7 @@ function renderActiveConnection(activeConnection) {
|
|
|
61
61
|
<p class="text-[10px] font-mono uppercase tracking-[0.24em] text-primary-container/70">
|
|
62
62
|
ACTIVE_DATABASE
|
|
63
63
|
</p>
|
|
64
|
-
<h2 class="mt-2 font-
|
|
64
|
+
<h2 class="mt-2 font-body text-2xl font-black uppercase tracking-tight text-primary-container">
|
|
65
65
|
${escapeHtml(activeConnection.label)}
|
|
66
66
|
</h2>
|
|
67
67
|
<p class="mt-2 font-mono text-[10px] text-on-surface-variant/55">${escapeHtml(
|
|
@@ -93,7 +93,7 @@ export function renderEmptyState({ activeConnection, recentConnections = [] }) {
|
|
|
93
93
|
SYSTEM_READY // ${hasActive ? 'ACTIVE_CONTEXT' : 'IDLE_STATE'}
|
|
94
94
|
</span>
|
|
95
95
|
</div>
|
|
96
|
-
<h1 class="mb-4 font-
|
|
96
|
+
<h1 class="mb-4 font-body text-7xl font-black tracking-tighter text-primary-container opacity-90 md:text-9xl">
|
|
97
97
|
SQLite Hub
|
|
98
98
|
</h1>
|
|
99
99
|
<p class="font-mono text-primary-container/40">The only fucking SQLite Manager you'll ever need.</p>
|
|
@@ -104,7 +104,7 @@ export function renderEmptyState({ activeConnection, recentConnections = [] }) {
|
|
|
104
104
|
</div>
|
|
105
105
|
<div class="mx-auto grid w-full max-w-3xl grid-cols-1 gap-4 px-6 md:grid-cols-2">
|
|
106
106
|
<button
|
|
107
|
-
class="landing-primary-action clipped-btn group flex items-center justify-between bg-primary-container px-8 py-6 font-
|
|
107
|
+
class="landing-primary-action clipped-btn group flex items-center justify-between bg-primary-container px-8 py-6 font-body text-lg font-bold transition-all duration-300 hover:shadow-[0_0_20px_rgba(252,227,0,0.3)]"
|
|
108
108
|
data-action="open-modal"
|
|
109
109
|
data-modal="open-connection"
|
|
110
110
|
style="--clip-path: polygon(0 0, 90% 0, 100% 25%, 100% 100%, 0 100%);"
|
|
@@ -114,7 +114,7 @@ export function renderEmptyState({ activeConnection, recentConnections = [] }) {
|
|
|
114
114
|
<span class="material-symbols-outlined transition-transform group-hover:translate-x-1">add_circle</span>
|
|
115
115
|
</button>
|
|
116
116
|
<button
|
|
117
|
-
class="flex items-center justify-between border-l-2 border-primary-container bg-surface-container-highest px-8 py-6 font-
|
|
117
|
+
class="flex items-center justify-between border-l-2 border-primary-container bg-surface-container-highest px-8 py-6 font-body text-lg font-bold text-primary-container transition-colors duration-150 hover:bg-surface-bright"
|
|
118
118
|
data-action="open-modal"
|
|
119
119
|
data-modal="create-connection"
|
|
120
120
|
type="button"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { renderDropdownButton } from './dropdownButton.js';
|
|
2
|
+
|
|
3
|
+
export function renderGenerateTypesDropdown({ selectedTableName = '', hasTables = false } = {}) {
|
|
4
|
+
const hasSelectedTable = Boolean(String(selectedTableName ?? '').trim());
|
|
5
|
+
|
|
6
|
+
return renderDropdownButton({
|
|
7
|
+
align: 'left',
|
|
8
|
+
icon: 'code',
|
|
9
|
+
label: 'Generate Types',
|
|
10
|
+
title: 'Generate types',
|
|
11
|
+
items: [
|
|
12
|
+
{
|
|
13
|
+
action: 'open-generate-types-modal',
|
|
14
|
+
disabled: !hasSelectedTable,
|
|
15
|
+
icon: 'select_check_box',
|
|
16
|
+
label: 'Selected table',
|
|
17
|
+
dataAttributes: {
|
|
18
|
+
tableName: selectedTableName,
|
|
19
|
+
typeScope: 'selected',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
action: 'open-generate-types-modal',
|
|
24
|
+
disabled: !hasTables,
|
|
25
|
+
icon: 'library_books',
|
|
26
|
+
label: 'All tables',
|
|
27
|
+
dataAttributes: {
|
|
28
|
+
typeScope: 'all',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -10,7 +10,7 @@ export function renderMetricCard({
|
|
|
10
10
|
return `
|
|
11
11
|
<div class="metric-card ${accent ? "metric-card--accent" : ""}">
|
|
12
12
|
<span class="text-[10px] font-mono text-on-surface/40 uppercase">${escapeHtml(label)}</span>
|
|
13
|
-
<span class="text-3xl font-
|
|
13
|
+
<span class="text-3xl font-body font-bold text-on-surface">${escapeHtml(value)}</span>
|
|
14
14
|
${
|
|
15
15
|
progress
|
|
16
16
|
? `
|