sqlite-hub 0.12.0 → 0.17.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 +118 -23
- package/bin/sqlite-hub.js +1041 -224
- package/frontend/index.html +1 -0
- package/frontend/js/api.js +32 -2
- package/frontend/js/app.js +989 -13
- package/frontend/js/components/modal.js +644 -15
- package/frontend/js/components/pageHeader.js +14 -16
- package/frontend/js/components/queryEditor.js +9 -1
- package/frontend/js/components/queryHistoryPanel.js +126 -131
- package/frontend/js/components/queryResults.js +79 -17
- package/frontend/js/components/rowEditorPanel.js +204 -10
- package/frontend/js/components/sidebar.js +102 -18
- package/frontend/js/components/tableDesignerEditor.js +69 -11
- package/frontend/js/router.js +8 -0
- package/frontend/js/store.js +868 -9
- 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/markdownDocuments.js +248 -0
- 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 +34 -2
- package/frontend/js/views/documents.js +300 -0
- package/frontend/js/views/editor.js +48 -1
- package/frontend/js/views/settings.js +39 -6
- package/frontend/js/views/structure.js +154 -212
- package/frontend/styles/base.css +6 -0
- package/frontend/styles/components.css +476 -2
- package/frontend/styles/structure-graph.css +0 -3
- package/frontend/styles/tailwind.generated.css +1 -1
- package/frontend/styles/tokens.css +1 -1
- package/frontend/styles/views.css +422 -0
- package/package.json +3 -3
- package/server/routes/documents.js +163 -0
- package/server/routes/settings.js +22 -6
- package/server/server.js +6 -0
- package/server/services/sqlite/introspection.js +10 -0
- package/server/services/sqlite/sqlExecutor.js +29 -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/storage/appStateStore.js +313 -0
- package/tests/check-constraint-options.test.js +14 -0
- package/tests/cli-args.test.js +100 -0
- package/tests/copy-column-modal.test.js +83 -0
- package/tests/database-documents.test.js +85 -0
- package/tests/export-filenames.test.js +34 -0
- package/tests/file-path-preview.test.js +165 -0
- package/tests/markdown-documents.test.js +79 -0
- package/tests/row-editor-json.test.js +82 -0
- package/tests/row-editor-timestamp-preview.test.js +192 -0
- package/tests/settings-metadata.test.js +16 -0
- package/tests/settings-view.test.js +32 -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
- package/fill.js +0 -526
package/frontend/js/app.js
CHANGED
|
@@ -24,6 +24,8 @@ import { renderTopNav } from './components/topNav.js';
|
|
|
24
24
|
import { createRouter } from './router.js';
|
|
25
25
|
import {
|
|
26
26
|
createActiveConnectionBackup,
|
|
27
|
+
createDocument,
|
|
28
|
+
createDocumentFromMarkdownExport,
|
|
27
29
|
clearCurrentQuery,
|
|
28
30
|
clearDataRowSelection,
|
|
29
31
|
clearEditorRowSelection,
|
|
@@ -48,11 +50,16 @@ import {
|
|
|
48
50
|
openDeleteQueryHistoryModal,
|
|
49
51
|
openDeleteQueryChartModal,
|
|
50
52
|
openDataExportModal,
|
|
53
|
+
openDeleteDocumentModal,
|
|
51
54
|
openQueryExportModal,
|
|
52
55
|
openDataRowByIdentity,
|
|
53
56
|
openEditConnectionModal,
|
|
54
57
|
openCreateQueryChartModal,
|
|
58
|
+
openCopyColumnModal,
|
|
55
59
|
openEditQueryChartModal,
|
|
60
|
+
openDocumentInsertNoteModal,
|
|
61
|
+
openDocumentInsertTableModal,
|
|
62
|
+
preserveCurrentDataRowSelectionForReload,
|
|
56
63
|
openDataRowUpdatePreview,
|
|
57
64
|
openEditorRowUpdatePreview,
|
|
58
65
|
refreshCurrentRoute,
|
|
@@ -64,6 +71,7 @@ import {
|
|
|
64
71
|
runQueryHistoryItem,
|
|
65
72
|
skipCurrentMediaTaggingItem,
|
|
66
73
|
saveCurrentQueryChartDraft,
|
|
74
|
+
saveCurrentDocument,
|
|
67
75
|
saveCurrentMediaTaggingConfig,
|
|
68
76
|
selectDataRow,
|
|
69
77
|
selectEditorRow,
|
|
@@ -88,6 +96,9 @@ import {
|
|
|
88
96
|
setEditorPanelVisibility,
|
|
89
97
|
setEditorTab,
|
|
90
98
|
submitDeleteChartConfirmation,
|
|
99
|
+
submitDeleteDocumentConfirmation,
|
|
100
|
+
submitDocumentInsertNote,
|
|
101
|
+
submitDocumentInsertTable,
|
|
91
102
|
submitCreateMediaTaggingTagTable,
|
|
92
103
|
submitCreateMediaTaggingMappingTable,
|
|
93
104
|
submitDeleteQueryHistoryConfirmation,
|
|
@@ -97,16 +108,22 @@ import {
|
|
|
97
108
|
sortEditorResultsByColumn,
|
|
98
109
|
setQueryHistorySearchInput,
|
|
99
110
|
setQueryHistoryTab,
|
|
111
|
+
setCopyColumnModalError,
|
|
112
|
+
setCopyColumnModalEditedText,
|
|
113
|
+
setCopyColumnModalSubmitting,
|
|
100
114
|
setRoute,
|
|
101
115
|
saveQueryHistoryNotes,
|
|
102
116
|
saveQueryHistoryTitle,
|
|
103
117
|
saveCurrentTableDesignerDraft,
|
|
104
118
|
toggleChartsResultsPanel,
|
|
105
119
|
toggleChartsSqlPanel,
|
|
120
|
+
toggleCurrentDocumentTodo,
|
|
121
|
+
toggleDocumentsPane,
|
|
106
122
|
setMediaTaggingWorkflowMediaDetailsVisible,
|
|
107
123
|
setMediaTaggingWorkflowMediaRotationDegrees,
|
|
108
124
|
queueTableDesignerCsvImport,
|
|
109
125
|
showToast,
|
|
126
|
+
storeCopyColumnPreferences,
|
|
110
127
|
submitCreateConnection,
|
|
111
128
|
createCurrentMediaTag,
|
|
112
129
|
submitDeleteRowConfirmation,
|
|
@@ -118,9 +135,13 @@ import {
|
|
|
118
135
|
toggleQueryHistorySavedState,
|
|
119
136
|
updateCurrentMediaTaggingField,
|
|
120
137
|
updateCurrentMediaTaggingTagFormField,
|
|
138
|
+
updateCopyColumnModalFormatField,
|
|
139
|
+
updateCurrentDocumentDraftField,
|
|
140
|
+
updateDocumentInsertQuerySelection,
|
|
121
141
|
updateCurrentQueryChartDraftConfigField,
|
|
122
142
|
updateCurrentQueryChartDraftField,
|
|
123
143
|
updateCurrentTableDesignerColumnField,
|
|
144
|
+
updateCurrentTableDesignerConstraintField,
|
|
124
145
|
updateCurrentTableDesignerField,
|
|
125
146
|
addCurrentTableDesignerColumn,
|
|
126
147
|
applyCurrentMediaTaggingSelection,
|
|
@@ -129,6 +150,7 @@ import {
|
|
|
129
150
|
import { renderChartsDetail, renderChartsView } from './views/charts.js';
|
|
130
151
|
import { renderConnectionsView } from './views/connections.js';
|
|
131
152
|
import { renderDataRowEditorPanel, renderDataView } from './views/data.js';
|
|
153
|
+
import { renderDocumentsView } from './views/documents.js';
|
|
132
154
|
import { renderEditorView } from './views/editor.js';
|
|
133
155
|
import { renderLandingView } from './views/landing.js';
|
|
134
156
|
import { renderMediaTaggingView } from './views/mediaTagging.js';
|
|
@@ -137,7 +159,29 @@ import { renderSettingsView } from './views/settings.js';
|
|
|
137
159
|
import { renderStructureView } from './views/structure.js';
|
|
138
160
|
import { renderTableDesignerView } from './views/tableDesigner.js';
|
|
139
161
|
import { replaceChildrenFromRenderedMarkup, replaceElementFromRenderedMarkup } from './utils/dom.js';
|
|
140
|
-
import {
|
|
162
|
+
import {
|
|
163
|
+
buildCopyColumnText,
|
|
164
|
+
getCopyColumnExportMetadata,
|
|
165
|
+
isMarkdownTodoCopyColumnMode,
|
|
166
|
+
normalizeCopyColumnMode,
|
|
167
|
+
} from './utils/copyColumnExport.js';
|
|
168
|
+
import { formatNumber, highlightSql } from './utils/format.js';
|
|
169
|
+
import {
|
|
170
|
+
compactPathForDisplay,
|
|
171
|
+
detectFilePathValue,
|
|
172
|
+
getPathTypeLabel,
|
|
173
|
+
} from './utils/filePathPreview.js';
|
|
174
|
+
import { formatSqlQuery } from './utils/sqlFormatter.js';
|
|
175
|
+
import {
|
|
176
|
+
buildDataRowEditorJsonObject,
|
|
177
|
+
buildEditorRowEditorJsonObject,
|
|
178
|
+
stringifyRowEditorJson,
|
|
179
|
+
} from './utils/rowEditorJson.js';
|
|
180
|
+
import { getTimestampPreviewForField } from './utils/timestampPreview.js';
|
|
181
|
+
import {
|
|
182
|
+
formatTextCellCharacterCount,
|
|
183
|
+
getTextCellCharacterCount,
|
|
184
|
+
} from './utils/textCellStats.js';
|
|
141
185
|
|
|
142
186
|
const appRoot = document.querySelector('#app');
|
|
143
187
|
|
|
@@ -168,6 +212,10 @@ let lastRenderedLockedRoute = false;
|
|
|
168
212
|
let pendingNewTableDesignerAutofocus = false;
|
|
169
213
|
let pendingQueryEditorFocus = false;
|
|
170
214
|
let pendingMediaTaggingTagSearchFocus = false;
|
|
215
|
+
let documentAutosaveTimer = null;
|
|
216
|
+
let pendingDocumentAutosaveId = null;
|
|
217
|
+
|
|
218
|
+
const DOCUMENT_AUTOSAVE_DELAY_MS = 5000;
|
|
171
219
|
|
|
172
220
|
const APP_TITLE = 'SQLite Hub';
|
|
173
221
|
const ROUTE_TITLE_SEGMENTS = {
|
|
@@ -178,6 +226,7 @@ const ROUTE_TITLE_SEGMENTS = {
|
|
|
178
226
|
editor: 'SQL Editor',
|
|
179
227
|
editorResults: 'SQL Editor',
|
|
180
228
|
charts: 'Charts',
|
|
229
|
+
documents: 'Documents',
|
|
181
230
|
tableDesigner: 'Table Designer',
|
|
182
231
|
mediaTaggingSetup: 'Media Tagging',
|
|
183
232
|
mediaTaggingQueue: 'Tagging Queue',
|
|
@@ -768,13 +817,58 @@ function readFileAsText(file) {
|
|
|
768
817
|
resolve(String(reader.result ?? ''));
|
|
769
818
|
};
|
|
770
819
|
reader.onerror = () => {
|
|
771
|
-
reject(reader.error ?? new Error('The selected
|
|
820
|
+
reject(reader.error ?? new Error('The selected file could not be read.'));
|
|
772
821
|
};
|
|
773
822
|
|
|
774
823
|
reader.readAsText(file);
|
|
775
824
|
});
|
|
776
825
|
}
|
|
777
826
|
|
|
827
|
+
function clearDocumentAutosaveTimer() {
|
|
828
|
+
if (documentAutosaveTimer !== null) {
|
|
829
|
+
window.clearTimeout(documentAutosaveTimer);
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
documentAutosaveTimer = null;
|
|
833
|
+
pendingDocumentAutosaveId = null;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
function scheduleDocumentAutosave(documentId) {
|
|
837
|
+
clearDocumentAutosaveTimer();
|
|
838
|
+
|
|
839
|
+
if (!documentId) {
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
pendingDocumentAutosaveId = String(documentId);
|
|
844
|
+
documentAutosaveTimer = window.setTimeout(async () => {
|
|
845
|
+
const state = getState();
|
|
846
|
+
const scheduledDocumentId = pendingDocumentAutosaveId;
|
|
847
|
+
|
|
848
|
+
documentAutosaveTimer = null;
|
|
849
|
+
pendingDocumentAutosaveId = null;
|
|
850
|
+
|
|
851
|
+
if (
|
|
852
|
+
state.route.name !== 'documents' ||
|
|
853
|
+
String(state.documents.selectedId ?? '') !== scheduledDocumentId ||
|
|
854
|
+
!state.documents.dirty
|
|
855
|
+
) {
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
if (state.documents.saving) {
|
|
860
|
+
scheduleDocumentAutosave(scheduledDocumentId);
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
if (state.documents.deleting) {
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
await saveCurrentDocument({ toast: false });
|
|
869
|
+
}, DOCUMENT_AUTOSAVE_DELAY_MS);
|
|
870
|
+
}
|
|
871
|
+
|
|
778
872
|
async function buildConnectionLogoUpload(file) {
|
|
779
873
|
if (!(file instanceof File) || !file.size) {
|
|
780
874
|
return null;
|
|
@@ -823,6 +917,8 @@ function resolveView(state) {
|
|
|
823
917
|
return renderOverviewView(state);
|
|
824
918
|
case 'charts':
|
|
825
919
|
return renderChartsView(state);
|
|
920
|
+
case 'documents':
|
|
921
|
+
return renderDocumentsView(state);
|
|
826
922
|
case 'data':
|
|
827
923
|
return renderDataView(state);
|
|
828
924
|
case 'editor':
|
|
@@ -1072,6 +1168,39 @@ async function handleTableDesignerCsvImport(fileInput) {
|
|
|
1072
1168
|
}
|
|
1073
1169
|
}
|
|
1074
1170
|
|
|
1171
|
+
async function handleDocumentMarkdownImport(fileInput) {
|
|
1172
|
+
if (!(fileInput instanceof HTMLInputElement)) {
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
const file = fileInput.files?.[0];
|
|
1177
|
+
|
|
1178
|
+
if (!(file instanceof File)) {
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
try {
|
|
1183
|
+
const content = await readFileAsText(file);
|
|
1184
|
+
const document = await createDocumentFromMarkdownExport({
|
|
1185
|
+
filename: file.name,
|
|
1186
|
+
content,
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
if (!document?.id) {
|
|
1190
|
+
showToast('Markdown document could not be imported.', 'alert');
|
|
1191
|
+
return;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
clearDocumentAutosaveTimer();
|
|
1195
|
+
showToast(`Document "${document.filename}" imported.`, 'success');
|
|
1196
|
+
router.navigate(`/documents/${encodeURIComponent(document.id)}`);
|
|
1197
|
+
} catch (error) {
|
|
1198
|
+
showToast(error?.message || 'Markdown import failed.', 'alert');
|
|
1199
|
+
} finally {
|
|
1200
|
+
fileInput.value = '';
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1075
1204
|
function renderApp(state) {
|
|
1076
1205
|
syncDocumentTitle(state);
|
|
1077
1206
|
|
|
@@ -1089,6 +1218,7 @@ function renderApp(state) {
|
|
|
1089
1218
|
'editorResults',
|
|
1090
1219
|
'data',
|
|
1091
1220
|
'charts',
|
|
1221
|
+
'documents',
|
|
1092
1222
|
'structure',
|
|
1093
1223
|
'tableDesigner',
|
|
1094
1224
|
'mediaTaggingSetup',
|
|
@@ -1254,6 +1384,39 @@ async function executeEditorQueryAndNavigate() {
|
|
|
1254
1384
|
router.navigate(success && activeTab === 'results' ? '/editor/results' : '/editor');
|
|
1255
1385
|
}
|
|
1256
1386
|
|
|
1387
|
+
function formatCurrentQuery() {
|
|
1388
|
+
const currentQuery = getState().editor.sqlText ?? '';
|
|
1389
|
+
const formattedQuery = formatSqlQuery(currentQuery);
|
|
1390
|
+
|
|
1391
|
+
if (!formattedQuery) {
|
|
1392
|
+
showToast('No SQL query to format.', 'alert');
|
|
1393
|
+
return;
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
if (formattedQuery === currentQuery) {
|
|
1397
|
+
focusQueryEditorInput();
|
|
1398
|
+
showToast('SQL query is already formatted.', 'muted');
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
invalidateMainRenderCache();
|
|
1403
|
+
setCurrentQuery(formattedQuery);
|
|
1404
|
+
|
|
1405
|
+
const input = document.querySelector('[data-bind="current-query"]');
|
|
1406
|
+
|
|
1407
|
+
if (input instanceof HTMLTextAreaElement) {
|
|
1408
|
+
input.value = formattedQuery;
|
|
1409
|
+
syncQueryEditorHighlight(input);
|
|
1410
|
+
syncQueryEditorScroll(input);
|
|
1411
|
+
input.focus({ preventScroll: true });
|
|
1412
|
+
input.setSelectionRange(input.value.length, input.value.length);
|
|
1413
|
+
} else {
|
|
1414
|
+
pendingQueryEditorFocus = true;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
showToast('SQL query formatted.', 'success');
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1257
1420
|
const OPENABLE_URL_PATTERN = /^https?:\/\/[^\s<>"']+$/i;
|
|
1258
1421
|
|
|
1259
1422
|
function getOpenableUrl(value) {
|
|
@@ -1291,6 +1454,561 @@ function openRowEditorUrl(actionNode) {
|
|
|
1291
1454
|
link.remove();
|
|
1292
1455
|
}
|
|
1293
1456
|
|
|
1457
|
+
function getExportFilenameFromAction(actionNode) {
|
|
1458
|
+
const input = actionNode.closest('[data-export-modal]')?.querySelector('input[name="filename"]');
|
|
1459
|
+
return input instanceof HTMLInputElement ? input.value : '';
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
function syncRowEditorTimestampPreview(inputNode) {
|
|
1463
|
+
const fieldNode = inputNode.closest('[data-row-editor-field]');
|
|
1464
|
+
const previewNode = fieldNode?.querySelector('[data-row-editor-timestamp-preview]');
|
|
1465
|
+
|
|
1466
|
+
if (!fieldNode || !previewNode) {
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
const columnName = fieldNode.dataset.rowEditorColumnName ?? '';
|
|
1471
|
+
const protectedKeyColumn = fieldNode.dataset.rowEditorProtectedKey === 'true';
|
|
1472
|
+
const tableMeta = {
|
|
1473
|
+
columns: [
|
|
1474
|
+
{
|
|
1475
|
+
name: columnName,
|
|
1476
|
+
primaryKeyPosition: protectedKeyColumn ? 1 : 0,
|
|
1477
|
+
foreignKey: protectedKeyColumn,
|
|
1478
|
+
},
|
|
1479
|
+
],
|
|
1480
|
+
foreignKeys: [],
|
|
1481
|
+
};
|
|
1482
|
+
const preview = getTimestampPreviewForField({
|
|
1483
|
+
columnName,
|
|
1484
|
+
value: inputNode.value,
|
|
1485
|
+
tableMeta,
|
|
1486
|
+
});
|
|
1487
|
+
|
|
1488
|
+
if (preview.kind !== 'timestamp') {
|
|
1489
|
+
previewNode.hidden = true;
|
|
1490
|
+
previewNode.textContent = '';
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
previewNode.hidden = false;
|
|
1495
|
+
previewNode.textContent = `Interpretiert als Datum: ${preview.formatted}`;
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
function syncRowEditorCharacterCount(inputNode) {
|
|
1499
|
+
const fieldNode = inputNode.closest('[data-row-editor-field]');
|
|
1500
|
+
const countNode = fieldNode?.querySelector('[data-row-editor-char-count]');
|
|
1501
|
+
|
|
1502
|
+
if (!fieldNode || !countNode) {
|
|
1503
|
+
return;
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
const count = getTextCellCharacterCount(inputNode.value);
|
|
1507
|
+
|
|
1508
|
+
if (count === null) {
|
|
1509
|
+
countNode.hidden = true;
|
|
1510
|
+
countNode.textContent = '';
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
countNode.hidden = false;
|
|
1515
|
+
countNode.textContent = formatTextCellCharacterCount(count);
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
function syncRowEditorFilePathPreview(inputNode) {
|
|
1519
|
+
const fieldNode = inputNode.closest('[data-row-editor-field]');
|
|
1520
|
+
const previewNode = fieldNode?.querySelector('[data-row-editor-filepath-preview]');
|
|
1521
|
+
|
|
1522
|
+
if (!fieldNode || !previewNode) {
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
const columnName = fieldNode.dataset.rowEditorColumnName ?? '';
|
|
1527
|
+
const protectedKeyColumn = fieldNode.dataset.rowEditorProtectedKey === 'true';
|
|
1528
|
+
const tableMeta = {
|
|
1529
|
+
columns: [
|
|
1530
|
+
{
|
|
1531
|
+
name: columnName,
|
|
1532
|
+
primaryKeyPosition: protectedKeyColumn ? 1 : 0,
|
|
1533
|
+
foreignKey: protectedKeyColumn,
|
|
1534
|
+
},
|
|
1535
|
+
],
|
|
1536
|
+
foreignKeys: [],
|
|
1537
|
+
};
|
|
1538
|
+
const preview = detectFilePathValue(inputNode.value, columnName, tableMeta);
|
|
1539
|
+
|
|
1540
|
+
if (!preview) {
|
|
1541
|
+
previewNode.hidden = true;
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
const filenameNode = previewNode.querySelector('[data-row-editor-filepath-filename]');
|
|
1546
|
+
const directoryRowNode = previewNode.querySelector('[data-row-editor-filepath-directory-row]');
|
|
1547
|
+
const directoryNode = previewNode.querySelector('[data-row-editor-filepath-directory]');
|
|
1548
|
+
const extensionNode = previewNode.querySelector('[data-row-editor-filepath-extension]');
|
|
1549
|
+
const typeNode = previewNode.querySelector('[data-row-editor-filepath-type]');
|
|
1550
|
+
|
|
1551
|
+
if (filenameNode) {
|
|
1552
|
+
filenameNode.textContent = preview.fileName ?? 'N/A';
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
if (directoryRowNode) {
|
|
1556
|
+
directoryRowNode.hidden = !preview.directory;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
if (directoryNode) {
|
|
1560
|
+
directoryNode.textContent = preview.directory ? compactPathForDisplay(preview.directory, 72) : '';
|
|
1561
|
+
directoryNode.setAttribute('title', preview.directory ?? '');
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
if (extensionNode) {
|
|
1565
|
+
extensionNode.textContent = preview.extension ?? 'N/A';
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
if (typeNode) {
|
|
1569
|
+
typeNode.textContent = getPathTypeLabel(preview.pathType);
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
previewNode.hidden = false;
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
function closeCopyColumnMenus(exceptMenu = null) {
|
|
1576
|
+
document.querySelectorAll('[data-copy-column-menu][open]').forEach(menu => {
|
|
1577
|
+
if (menu !== exceptMenu && menu instanceof HTMLDetailsElement) {
|
|
1578
|
+
menu.open = false;
|
|
1579
|
+
}
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
function closeSidebarDatabasePickers(exceptPicker = null) {
|
|
1584
|
+
document.querySelectorAll('.sidebar-db-picker[open]').forEach(picker => {
|
|
1585
|
+
if (picker !== exceptPicker && picker instanceof HTMLDetailsElement) {
|
|
1586
|
+
picker.open = false;
|
|
1587
|
+
}
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
function openCopyColumnMenu(headerNode) {
|
|
1592
|
+
const menu = headerNode?.querySelector('[data-copy-column-menu]');
|
|
1593
|
+
|
|
1594
|
+
if (!(menu instanceof HTMLDetailsElement)) {
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
closeCopyColumnMenus(menu);
|
|
1599
|
+
menu.open = true;
|
|
1600
|
+
menu.querySelector('summary')?.focus({ preventScroll: true });
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
function getCopyColumnResult(state, scope) {
|
|
1604
|
+
return scope === 'charts' ? state.charts.result : state.editor.result;
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
function slugifyExportFilenamePart(value, fallback = 'column') {
|
|
1608
|
+
const slug = String(value ?? '')
|
|
1609
|
+
.trim()
|
|
1610
|
+
.toLowerCase()
|
|
1611
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
1612
|
+
.replace(/^-+|-+$/g, '');
|
|
1613
|
+
|
|
1614
|
+
return slug || fallback;
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
function buildCopyColumnExportFilename(columnName, copyMode) {
|
|
1618
|
+
const metadata = getCopyColumnExportMetadata(copyMode);
|
|
1619
|
+
const columnSlug = slugifyExportFilenamePart(columnName);
|
|
1620
|
+
|
|
1621
|
+
return `${columnSlug}-${metadata.suffix}.${metadata.extension}`;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
function normalizeMarkdownDownloadFilename(value) {
|
|
1625
|
+
let filename = String(value ?? '')
|
|
1626
|
+
.trim()
|
|
1627
|
+
.replace(/[\u0000-\u001f\u007f]/g, ' ')
|
|
1628
|
+
.replace(/[\\/]+/g, ' ')
|
|
1629
|
+
.replace(/\s+/g, ' ')
|
|
1630
|
+
.replace(/^\.+/, '')
|
|
1631
|
+
.trim();
|
|
1632
|
+
|
|
1633
|
+
if (!filename) {
|
|
1634
|
+
filename = 'document.md';
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
if (!/\.md$/i.test(filename)) {
|
|
1638
|
+
filename = `${filename}.md`;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
return filename;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
function countEditedMarkdownTodoItems(text) {
|
|
1645
|
+
const lines = String(text ?? '')
|
|
1646
|
+
.split(/\r\n|\r|\n/g)
|
|
1647
|
+
.filter(line => line.trim());
|
|
1648
|
+
|
|
1649
|
+
return lines.length;
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
function buildDocumentTimestampSlug(date = new Date()) {
|
|
1653
|
+
const pad = value => String(value).padStart(2, '0');
|
|
1654
|
+
|
|
1655
|
+
return [
|
|
1656
|
+
date.getFullYear(),
|
|
1657
|
+
pad(date.getMonth() + 1),
|
|
1658
|
+
pad(date.getDate()),
|
|
1659
|
+
pad(date.getHours()),
|
|
1660
|
+
pad(date.getMinutes()),
|
|
1661
|
+
].join('-');
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
function buildCopyColumnDocumentFilename(columnName) {
|
|
1665
|
+
const columnSlug = slugifyExportFilenamePart(columnName);
|
|
1666
|
+
|
|
1667
|
+
return `${columnSlug}-todos-${buildDocumentTimestampSlug()}.md`;
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
function buildCopyColumnDocumentContent({ state, columnName, text, valueCount }) {
|
|
1671
|
+
const activeConnection = state.connections.active;
|
|
1672
|
+
const lines = [
|
|
1673
|
+
`# ${columnName || 'Column'} todos`,
|
|
1674
|
+
'',
|
|
1675
|
+
`- Database: ${activeConnection?.label || 'Active database'}`,
|
|
1676
|
+
`- Column: ${columnName || 'N/A'}`,
|
|
1677
|
+
`- Items: ${valueCount}`,
|
|
1678
|
+
`- Generated: ${new Date().toLocaleString()}`,
|
|
1679
|
+
'',
|
|
1680
|
+
];
|
|
1681
|
+
|
|
1682
|
+
lines.push('## Todos', '', String(text ?? '').trim(), '');
|
|
1683
|
+
|
|
1684
|
+
return lines.join('\n');
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
function downloadTextFile({ text, filename, mimeType }) {
|
|
1688
|
+
const blob = new Blob([text], { type: mimeType });
|
|
1689
|
+
const url = URL.createObjectURL(blob);
|
|
1690
|
+
const link = document.createElement('a');
|
|
1691
|
+
|
|
1692
|
+
link.href = url;
|
|
1693
|
+
link.download = filename;
|
|
1694
|
+
document.body.appendChild(link);
|
|
1695
|
+
link.click();
|
|
1696
|
+
link.remove();
|
|
1697
|
+
URL.revokeObjectURL(url);
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
function getSelectedDataBrowserRowForJson(state) {
|
|
1701
|
+
if (state.dataBrowser.selectedRow) {
|
|
1702
|
+
return state.dataBrowser.selectedRow;
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
const rowIndex = state.dataBrowser.selectedRowIndex;
|
|
1706
|
+
|
|
1707
|
+
if (typeof rowIndex !== 'number') {
|
|
1708
|
+
return null;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
return state.dataBrowser.table?.rows?.[rowIndex] ?? null;
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
function getSelectedEditorResultRowForJson(state) {
|
|
1715
|
+
const rowIndex = state.editor.selectedRowIndex;
|
|
1716
|
+
|
|
1717
|
+
if (typeof rowIndex !== 'number') {
|
|
1718
|
+
return null;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
return state.editor.result?.rows?.[rowIndex] ?? null;
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
function applyChangedRowEditorFormValues(rowObject) {
|
|
1725
|
+
const form = shellRefs.panel.querySelector(
|
|
1726
|
+
'form[data-form="save-data-row"], form[data-form="save-editor-row"]',
|
|
1727
|
+
);
|
|
1728
|
+
|
|
1729
|
+
if (!form) {
|
|
1730
|
+
return rowObject;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
const nextRowObject = { ...rowObject };
|
|
1734
|
+
const formData = new FormData(form);
|
|
1735
|
+
|
|
1736
|
+
for (const [key, value] of formData.entries()) {
|
|
1737
|
+
if (!key.startsWith('field:')) {
|
|
1738
|
+
continue;
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
const control = Array.from(form.elements).find(element => element.name === key);
|
|
1742
|
+
const initialValue = control?.dataset?.rowEditorInitialValue;
|
|
1743
|
+
const currentValue = String(value ?? '');
|
|
1744
|
+
|
|
1745
|
+
if (initialValue !== undefined && currentValue === initialValue) {
|
|
1746
|
+
continue;
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
nextRowObject[key.slice('field:'.length)] = currentValue;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
return nextRowObject;
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
function buildRowEditorJsonPayload(state) {
|
|
1756
|
+
if (state.route.name === 'data') {
|
|
1757
|
+
const row = getSelectedDataBrowserRowForJson(state);
|
|
1758
|
+
const table = state.dataBrowser.table;
|
|
1759
|
+
const tableName = table?.name ?? state.dataBrowser.selectedTable ?? 'row';
|
|
1760
|
+
|
|
1761
|
+
if (!row || !table) {
|
|
1762
|
+
return null;
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
const rowObject = applyChangedRowEditorFormValues(
|
|
1766
|
+
buildDataRowEditorJsonObject({
|
|
1767
|
+
row,
|
|
1768
|
+
columns: table.columns ?? table.columnMeta ?? [],
|
|
1769
|
+
}),
|
|
1770
|
+
);
|
|
1771
|
+
|
|
1772
|
+
return {
|
|
1773
|
+
filename: `${slugifyExportFilenamePart(tableName, 'row')}-row.json`,
|
|
1774
|
+
label: tableName,
|
|
1775
|
+
text: stringifyRowEditorJson(rowObject),
|
|
1776
|
+
};
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
if (state.route.name === 'editorResults') {
|
|
1780
|
+
const row = getSelectedEditorResultRowForJson(state);
|
|
1781
|
+
const result = state.editor.result;
|
|
1782
|
+
const rowIndex = state.editor.selectedRowIndex;
|
|
1783
|
+
const tableName = result?.editing?.tableName ?? 'query-result';
|
|
1784
|
+
|
|
1785
|
+
if (!row || !result) {
|
|
1786
|
+
return null;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
const rowObject = applyChangedRowEditorFormValues(
|
|
1790
|
+
buildEditorRowEditorJsonObject({
|
|
1791
|
+
row,
|
|
1792
|
+
editingColumns: result.editing?.columns ?? [],
|
|
1793
|
+
resultColumns: result.columns ?? [],
|
|
1794
|
+
}),
|
|
1795
|
+
);
|
|
1796
|
+
|
|
1797
|
+
return {
|
|
1798
|
+
filename: `${slugifyExportFilenamePart(tableName, 'query-result')}-row-${Number(rowIndex) + 1}.json`,
|
|
1799
|
+
label: tableName,
|
|
1800
|
+
text: stringifyRowEditorJson(rowObject),
|
|
1801
|
+
};
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
return null;
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
async function copyRowEditorJson() {
|
|
1808
|
+
const payload = buildRowEditorJsonPayload(getState());
|
|
1809
|
+
|
|
1810
|
+
if (!payload) {
|
|
1811
|
+
showToast('No row is selected.', 'alert');
|
|
1812
|
+
return;
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
if (!navigator.clipboard?.writeText) {
|
|
1816
|
+
showToast('Clipboard API is not available.', 'alert');
|
|
1817
|
+
return;
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
try {
|
|
1821
|
+
await navigator.clipboard.writeText(payload.text);
|
|
1822
|
+
showToast(`Row from "${payload.label}" copied as JSON.`, 'success');
|
|
1823
|
+
} catch (error) {
|
|
1824
|
+
showToast('Row JSON could not be copied.', 'alert');
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
function exportRowEditorJson() {
|
|
1829
|
+
const payload = buildRowEditorJsonPayload(getState());
|
|
1830
|
+
|
|
1831
|
+
if (!payload) {
|
|
1832
|
+
showToast('No row is selected.', 'alert');
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
downloadTextFile({
|
|
1837
|
+
text: payload.text,
|
|
1838
|
+
filename: payload.filename,
|
|
1839
|
+
mimeType: 'application/json;charset=utf-8',
|
|
1840
|
+
});
|
|
1841
|
+
showToast(`Row from "${payload.label}" exported as JSON.`, 'success');
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
function exportCurrentDocumentMarkdown() {
|
|
1845
|
+
const documents = getState().documents;
|
|
1846
|
+
|
|
1847
|
+
if (!documents.selectedId) {
|
|
1848
|
+
showToast('No document is selected.', 'alert');
|
|
1849
|
+
return;
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
const filename = normalizeMarkdownDownloadFilename(documents.draftFilename || documents.selected?.filename);
|
|
1853
|
+
|
|
1854
|
+
downloadTextFile({
|
|
1855
|
+
text: documents.draftContent ?? '',
|
|
1856
|
+
filename,
|
|
1857
|
+
mimeType: 'text/markdown;charset=utf-8',
|
|
1858
|
+
});
|
|
1859
|
+
showToast(`Document "${filename}" exported.`, 'success');
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
function getCurrentDocumentEditorInsertionRange() {
|
|
1863
|
+
const textarea = document.querySelector('.documents-editor-input');
|
|
1864
|
+
|
|
1865
|
+
if (!(textarea instanceof HTMLTextAreaElement)) {
|
|
1866
|
+
return null;
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
return {
|
|
1870
|
+
start: textarea.selectionStart,
|
|
1871
|
+
end: textarea.selectionEnd,
|
|
1872
|
+
};
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
function preserveDataRowEditorSelectionForReload() {
|
|
1876
|
+
preserveCurrentDataRowSelectionForReload();
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
async function submitCopyColumnModal(formData) {
|
|
1880
|
+
const state = getState();
|
|
1881
|
+
const modal = state.modal;
|
|
1882
|
+
|
|
1883
|
+
if (modal?.kind !== 'copy-column') {
|
|
1884
|
+
return;
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
const scope = String(formData.get('scope') ?? modal.scope ?? 'editor');
|
|
1888
|
+
const columnName = String(formData.get('columnName') ?? modal.columnName ?? '');
|
|
1889
|
+
const copyMode = normalizeCopyColumnMode(String(formData.get('copyMode') ?? modal.copyMode ?? 'column'));
|
|
1890
|
+
const separator = String(formData.get('separator') ?? ',');
|
|
1891
|
+
const wrapper = String(formData.get('wrapper') ?? '');
|
|
1892
|
+
const lineBreaks = formData.get('lineBreaks') === 'on';
|
|
1893
|
+
const outputSeparator = lineBreaks ? '\n' : separator;
|
|
1894
|
+
const intent = String(formData.get('intent') ?? 'copy');
|
|
1895
|
+
const isExportIntent = intent === 'export';
|
|
1896
|
+
const isDocumentIntent = intent === 'document';
|
|
1897
|
+
const isMarkdownTodo = isMarkdownTodoCopyColumnMode(copyMode);
|
|
1898
|
+
const editedText = formData.has('editedText') ? String(formData.get('editedText') ?? '') : null;
|
|
1899
|
+
const result = getCopyColumnResult(state, scope);
|
|
1900
|
+
const hasColumn = (result?.columns ?? []).some(column => String(column) === columnName);
|
|
1901
|
+
|
|
1902
|
+
if (!hasColumn) {
|
|
1903
|
+
setCopyColumnModalError({
|
|
1904
|
+
code: 'COPY_COLUMN_UNAVAILABLE',
|
|
1905
|
+
message: 'Column is no longer available in the current result set.',
|
|
1906
|
+
});
|
|
1907
|
+
showToast('Column could not be copied.', 'alert');
|
|
1908
|
+
return;
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
if (!isMarkdownTodo) {
|
|
1912
|
+
storeCopyColumnPreferences({ separator, wrapper, lineBreaks });
|
|
1913
|
+
} else if (editedText !== null) {
|
|
1914
|
+
setCopyColumnModalEditedText(editedText);
|
|
1915
|
+
}
|
|
1916
|
+
setCopyColumnModalSubmitting(true);
|
|
1917
|
+
|
|
1918
|
+
const generatedOutput = buildCopyColumnText({
|
|
1919
|
+
result,
|
|
1920
|
+
columnName,
|
|
1921
|
+
copyMode,
|
|
1922
|
+
separator: outputSeparator,
|
|
1923
|
+
wrapper,
|
|
1924
|
+
});
|
|
1925
|
+
const text = isMarkdownTodo && editedText !== null ? editedText : generatedOutput.text;
|
|
1926
|
+
const valueCount = isMarkdownTodo && editedText !== null
|
|
1927
|
+
? countEditedMarkdownTodoItems(editedText)
|
|
1928
|
+
: generatedOutput.valueCount;
|
|
1929
|
+
|
|
1930
|
+
try {
|
|
1931
|
+
if (isDocumentIntent) {
|
|
1932
|
+
if (!isMarkdownTodo) {
|
|
1933
|
+
throw new Error('Document export is only available for Markdown Todo columns.');
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
const document = await createDocumentFromMarkdownExport({
|
|
1937
|
+
filename: buildCopyColumnDocumentFilename(columnName),
|
|
1938
|
+
title: `${columnName || 'Column'} todos`,
|
|
1939
|
+
content: buildCopyColumnDocumentContent({
|
|
1940
|
+
state,
|
|
1941
|
+
columnName,
|
|
1942
|
+
text,
|
|
1943
|
+
valueCount,
|
|
1944
|
+
}),
|
|
1945
|
+
});
|
|
1946
|
+
|
|
1947
|
+
if (!document) {
|
|
1948
|
+
throw new Error('Document could not be created.');
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
closeModal();
|
|
1952
|
+
showToast(
|
|
1953
|
+
`Document "${document.filename}" created · ${formatNumber(valueCount)} ${
|
|
1954
|
+
valueCount === 1 ? 'item' : 'items'
|
|
1955
|
+
}`,
|
|
1956
|
+
'success',
|
|
1957
|
+
);
|
|
1958
|
+
router.navigate(`/documents/${encodeURIComponent(document.id)}`);
|
|
1959
|
+
return;
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
if (isExportIntent) {
|
|
1963
|
+
const metadata = getCopyColumnExportMetadata(copyMode);
|
|
1964
|
+
|
|
1965
|
+
downloadTextFile({
|
|
1966
|
+
text,
|
|
1967
|
+
filename: buildCopyColumnExportFilename(columnName, copyMode),
|
|
1968
|
+
mimeType: metadata.mimeType,
|
|
1969
|
+
});
|
|
1970
|
+
closeModal();
|
|
1971
|
+
showToast(
|
|
1972
|
+
`Column "${columnName}" exported as ${metadata.label} · ${formatNumber(valueCount)} ${
|
|
1973
|
+
valueCount === 1 ? 'value' : 'values'
|
|
1974
|
+
}`,
|
|
1975
|
+
'success',
|
|
1976
|
+
);
|
|
1977
|
+
return;
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
if (!navigator.clipboard?.writeText) {
|
|
1981
|
+
throw new Error('Clipboard API is not available.');
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
await navigator.clipboard.writeText(text);
|
|
1985
|
+
closeModal();
|
|
1986
|
+
showToast(
|
|
1987
|
+
`Column "${columnName}" copied · ${formatNumber(valueCount)} ${valueCount === 1 ? 'value' : 'values'}`,
|
|
1988
|
+
'success',
|
|
1989
|
+
);
|
|
1990
|
+
} catch (error) {
|
|
1991
|
+
setCopyColumnModalError({
|
|
1992
|
+
code: isDocumentIntent
|
|
1993
|
+
? 'COPY_COLUMN_DOCUMENT_EXPORT_FAILED'
|
|
1994
|
+
: isExportIntent
|
|
1995
|
+
? 'COPY_COLUMN_EXPORT_FAILED'
|
|
1996
|
+
: 'CLIPBOARD_ACCESS_FAILED',
|
|
1997
|
+
message:
|
|
1998
|
+
error?.message ||
|
|
1999
|
+
(isDocumentIntent
|
|
2000
|
+
? 'Document export failed.'
|
|
2001
|
+
: isExportIntent
|
|
2002
|
+
? 'Column export failed.'
|
|
2003
|
+
: 'Clipboard access failed.'),
|
|
2004
|
+
});
|
|
2005
|
+
showToast(
|
|
2006
|
+
isDocumentIntent ? 'Document export failed.' : isExportIntent ? 'Column export failed.' : 'Clipboard access failed.',
|
|
2007
|
+
'alert',
|
|
2008
|
+
);
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
|
|
1294
2012
|
async function handleAction(actionNode) {
|
|
1295
2013
|
const { action } = actionNode.dataset;
|
|
1296
2014
|
|
|
@@ -1304,8 +2022,77 @@ async function handleAction(actionNode) {
|
|
|
1304
2022
|
case 'open-row-editor-url':
|
|
1305
2023
|
openRowEditorUrl(actionNode);
|
|
1306
2024
|
return;
|
|
2025
|
+
case 'copy-row-editor-json':
|
|
2026
|
+
await copyRowEditorJson();
|
|
2027
|
+
return;
|
|
2028
|
+
case 'export-row-editor-json':
|
|
2029
|
+
exportRowEditorJson();
|
|
2030
|
+
return;
|
|
1307
2031
|
case 'open-modal':
|
|
1308
|
-
openModal(actionNode.dataset.modal
|
|
2032
|
+
openModal(actionNode.dataset.modal, {
|
|
2033
|
+
columnId: actionNode.dataset.columnId,
|
|
2034
|
+
columnName: actionNode.dataset.columnName,
|
|
2035
|
+
});
|
|
2036
|
+
return;
|
|
2037
|
+
case 'open-copy-column-modal':
|
|
2038
|
+
closeCopyColumnMenus();
|
|
2039
|
+
openCopyColumnModal({
|
|
2040
|
+
scope: actionNode.dataset.resultScope,
|
|
2041
|
+
columnName: actionNode.dataset.columnName ?? '',
|
|
2042
|
+
mode: actionNode.dataset.copyMode,
|
|
2043
|
+
});
|
|
2044
|
+
return;
|
|
2045
|
+
case 'create-document': {
|
|
2046
|
+
const document = await createDocument();
|
|
2047
|
+
|
|
2048
|
+
if (document?.id) {
|
|
2049
|
+
router.navigate(`/documents/${encodeURIComponent(document.id)}`);
|
|
2050
|
+
}
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
case 'select-document': {
|
|
2054
|
+
const documentId = String(actionNode.dataset.documentId ?? '').trim();
|
|
2055
|
+
|
|
2056
|
+
if (documentId) {
|
|
2057
|
+
clearDocumentAutosaveTimer();
|
|
2058
|
+
router.navigate(`/documents/${encodeURIComponent(documentId)}`);
|
|
2059
|
+
}
|
|
2060
|
+
return;
|
|
2061
|
+
}
|
|
2062
|
+
case 'save-document':
|
|
2063
|
+
clearDocumentAutosaveTimer();
|
|
2064
|
+
await saveCurrentDocument();
|
|
2065
|
+
return;
|
|
2066
|
+
case 'export-document-markdown':
|
|
2067
|
+
exportCurrentDocumentMarkdown();
|
|
2068
|
+
return;
|
|
2069
|
+
case 'open-document-insert-table-modal':
|
|
2070
|
+
await openDocumentInsertTableModal(getCurrentDocumentEditorInsertionRange());
|
|
2071
|
+
return;
|
|
2072
|
+
case 'open-document-insert-note-modal':
|
|
2073
|
+
await openDocumentInsertNoteModal(getCurrentDocumentEditorInsertionRange());
|
|
2074
|
+
return;
|
|
2075
|
+
case 'import-document-markdown': {
|
|
2076
|
+
const fileInput = document.querySelector('[data-bind="document-import-file"]');
|
|
2077
|
+
|
|
2078
|
+
if (!(fileInput instanceof HTMLInputElement)) {
|
|
2079
|
+
return;
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
fileInput.value = '';
|
|
2083
|
+
fileInput.click();
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
2086
|
+
case 'delete-document':
|
|
2087
|
+
clearDocumentAutosaveTimer();
|
|
2088
|
+
openDeleteDocumentModal();
|
|
2089
|
+
return;
|
|
2090
|
+
case 'toggle-document-pane':
|
|
2091
|
+
toggleDocumentsPane(actionNode.dataset.pane);
|
|
2092
|
+
return;
|
|
2093
|
+
case 'toggle-document-todo':
|
|
2094
|
+
clearDocumentAutosaveTimer();
|
|
2095
|
+
await toggleCurrentDocumentTodo(actionNode.dataset.lineIndex);
|
|
1309
2096
|
return;
|
|
1310
2097
|
case 'open-create-query-chart-modal':
|
|
1311
2098
|
openCreateQueryChartModal();
|
|
@@ -1329,6 +2116,7 @@ async function handleAction(actionNode) {
|
|
|
1329
2116
|
dismissToast(actionNode.dataset.toastId);
|
|
1330
2117
|
return;
|
|
1331
2118
|
case 'select-connection': {
|
|
2119
|
+
closeSidebarDatabasePickers();
|
|
1332
2120
|
resetStructureGraphForDatabaseChange();
|
|
1333
2121
|
const next = await selectConnection(actionNode.dataset.connectionId);
|
|
1334
2122
|
if (next) {
|
|
@@ -1384,6 +2172,9 @@ async function handleAction(actionNode) {
|
|
|
1384
2172
|
await executeEditorQueryAndNavigate();
|
|
1385
2173
|
return;
|
|
1386
2174
|
}
|
|
2175
|
+
case 'format-current-query':
|
|
2176
|
+
formatCurrentQuery();
|
|
2177
|
+
return;
|
|
1387
2178
|
case 'delete-data-row':
|
|
1388
2179
|
openDeleteDataRowModal(actionNode.dataset.rowIndex);
|
|
1389
2180
|
return;
|
|
@@ -1507,9 +2298,10 @@ async function handleAction(actionNode) {
|
|
|
1507
2298
|
return;
|
|
1508
2299
|
case 'export-query-format': {
|
|
1509
2300
|
const format = actionNode.dataset.exportFormat;
|
|
2301
|
+
const filename = getExportFilenameFromAction(actionNode);
|
|
1510
2302
|
|
|
1511
2303
|
if (format === 'table') {
|
|
1512
|
-
const imported = await duplicateCurrentQueryAsTable();
|
|
2304
|
+
const imported = await duplicateCurrentQueryAsTable(filename);
|
|
1513
2305
|
|
|
1514
2306
|
if (imported) {
|
|
1515
2307
|
router.navigate('/table-designer/new');
|
|
@@ -1518,7 +2310,7 @@ async function handleAction(actionNode) {
|
|
|
1518
2310
|
return;
|
|
1519
2311
|
}
|
|
1520
2312
|
|
|
1521
|
-
await exportCurrentQueryFormat(format);
|
|
2313
|
+
await exportCurrentQueryFormat(format, filename);
|
|
1522
2314
|
return;
|
|
1523
2315
|
}
|
|
1524
2316
|
case 'open-data-export-modal':
|
|
@@ -1526,9 +2318,10 @@ async function handleAction(actionNode) {
|
|
|
1526
2318
|
return;
|
|
1527
2319
|
case 'export-data-format': {
|
|
1528
2320
|
const format = actionNode.dataset.exportFormat;
|
|
2321
|
+
const filename = getExportFilenameFromAction(actionNode);
|
|
1529
2322
|
|
|
1530
2323
|
if (format === 'table') {
|
|
1531
|
-
const imported = await duplicateCurrentDataTableAsTable();
|
|
2324
|
+
const imported = await duplicateCurrentDataTableAsTable(filename);
|
|
1532
2325
|
|
|
1533
2326
|
if (imported) {
|
|
1534
2327
|
router.navigate('/table-designer/new');
|
|
@@ -1537,7 +2330,7 @@ async function handleAction(actionNode) {
|
|
|
1537
2330
|
return;
|
|
1538
2331
|
}
|
|
1539
2332
|
|
|
1540
|
-
await exportCurrentDataTableFormat(format);
|
|
2333
|
+
await exportCurrentDataTableFormat(format, filename);
|
|
1541
2334
|
return;
|
|
1542
2335
|
}
|
|
1543
2336
|
case 'toggle-data-tables':
|
|
@@ -1715,6 +2508,7 @@ async function handleAction(actionNode) {
|
|
|
1715
2508
|
}
|
|
1716
2509
|
return;
|
|
1717
2510
|
case 'reload-data-route':
|
|
2511
|
+
preserveDataRowEditorSelectionForReload();
|
|
1718
2512
|
await refreshCurrentRoute();
|
|
1719
2513
|
return;
|
|
1720
2514
|
default:
|
|
@@ -1735,7 +2529,37 @@ function canApplyMediaTaggingShortcut(state) {
|
|
|
1735
2529
|
}
|
|
1736
2530
|
|
|
1737
2531
|
document.addEventListener('click', event => {
|
|
1738
|
-
const
|
|
2532
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
2533
|
+
|
|
2534
|
+
if (!target) {
|
|
2535
|
+
return;
|
|
2536
|
+
}
|
|
2537
|
+
|
|
2538
|
+
const copyColumnMenu = target.closest('[data-copy-column-menu]');
|
|
2539
|
+
|
|
2540
|
+
if (!copyColumnMenu) {
|
|
2541
|
+
closeCopyColumnMenus();
|
|
2542
|
+
} else if (target.closest('.query-result-column-menu__toggle')) {
|
|
2543
|
+
window.requestAnimationFrame(() => {
|
|
2544
|
+
if (copyColumnMenu instanceof HTMLDetailsElement && copyColumnMenu.open) {
|
|
2545
|
+
closeCopyColumnMenus(copyColumnMenu);
|
|
2546
|
+
}
|
|
2547
|
+
});
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
const sidebarDatabasePicker = target.closest('.sidebar-db-picker');
|
|
2551
|
+
|
|
2552
|
+
if (!sidebarDatabasePicker) {
|
|
2553
|
+
closeSidebarDatabasePickers();
|
|
2554
|
+
} else if (target.closest('.sidebar-footer-card')) {
|
|
2555
|
+
window.requestAnimationFrame(() => {
|
|
2556
|
+
if (sidebarDatabasePicker instanceof HTMLDetailsElement && sidebarDatabasePicker.open) {
|
|
2557
|
+
closeSidebarDatabasePickers(sidebarDatabasePicker);
|
|
2558
|
+
}
|
|
2559
|
+
});
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2562
|
+
const actionNode = target.closest('[data-action]');
|
|
1739
2563
|
|
|
1740
2564
|
if (!actionNode) {
|
|
1741
2565
|
return;
|
|
@@ -1744,10 +2568,36 @@ document.addEventListener('click', event => {
|
|
|
1744
2568
|
handleAction(actionNode);
|
|
1745
2569
|
});
|
|
1746
2570
|
|
|
2571
|
+
document.addEventListener('contextmenu', event => {
|
|
2572
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
2573
|
+
const headerNode = target?.closest('[data-result-column-header]');
|
|
2574
|
+
|
|
2575
|
+
if (!headerNode) {
|
|
2576
|
+
return;
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2579
|
+
event.preventDefault();
|
|
2580
|
+
openCopyColumnMenu(headerNode);
|
|
2581
|
+
});
|
|
2582
|
+
|
|
1747
2583
|
document.addEventListener('keydown', event => {
|
|
1748
2584
|
const target = event.target;
|
|
1749
2585
|
const state = getState();
|
|
1750
2586
|
|
|
2587
|
+
if (
|
|
2588
|
+
(event.key === 's' || event.key === 'S') &&
|
|
2589
|
+
(event.ctrlKey || event.metaKey) &&
|
|
2590
|
+
!event.altKey &&
|
|
2591
|
+
!event.shiftKey &&
|
|
2592
|
+
!event.defaultPrevented &&
|
|
2593
|
+
state.route.name === 'documents'
|
|
2594
|
+
) {
|
|
2595
|
+
event.preventDefault();
|
|
2596
|
+
clearDocumentAutosaveTimer();
|
|
2597
|
+
void saveCurrentDocument();
|
|
2598
|
+
return;
|
|
2599
|
+
}
|
|
2600
|
+
|
|
1751
2601
|
// Handle Enter key in tag form fields to trigger create tag
|
|
1752
2602
|
if (
|
|
1753
2603
|
event.key === 'Enter' &&
|
|
@@ -1810,6 +2660,18 @@ document.addEventListener('keydown', event => {
|
|
|
1810
2660
|
return;
|
|
1811
2661
|
}
|
|
1812
2662
|
|
|
2663
|
+
if (document.querySelector('[data-copy-column-menu][open]')) {
|
|
2664
|
+
event.preventDefault();
|
|
2665
|
+
closeCopyColumnMenus();
|
|
2666
|
+
return;
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2669
|
+
if (document.querySelector('.sidebar-db-picker[open]')) {
|
|
2670
|
+
event.preventDefault();
|
|
2671
|
+
closeSidebarDatabasePickers();
|
|
2672
|
+
return;
|
|
2673
|
+
}
|
|
2674
|
+
|
|
1813
2675
|
if (
|
|
1814
2676
|
state.route.name === 'data' &&
|
|
1815
2677
|
(typeof state.dataBrowser.selectedRowIndex === 'number' || Boolean(state.dataBrowser.selectedRow))
|
|
@@ -1826,12 +2688,33 @@ document.addEventListener('keydown', event => {
|
|
|
1826
2688
|
});
|
|
1827
2689
|
|
|
1828
2690
|
document.addEventListener('input', event => {
|
|
2691
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
2692
|
+
const timestampInput = target?.closest('[data-row-editor-timestamp-source]');
|
|
2693
|
+
const textCellInput = target?.closest('[data-row-editor-text-source]');
|
|
2694
|
+
|
|
2695
|
+
if (timestampInput) {
|
|
2696
|
+
syncRowEditorTimestampPreview(timestampInput);
|
|
2697
|
+
syncRowEditorFilePathPreview(timestampInput);
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
if (textCellInput) {
|
|
2701
|
+
syncRowEditorCharacterCount(textCellInput);
|
|
2702
|
+
}
|
|
2703
|
+
|
|
1829
2704
|
const bindNode = event.target.closest('[data-bind]');
|
|
1830
2705
|
|
|
1831
2706
|
if (!bindNode) {
|
|
1832
2707
|
return;
|
|
1833
2708
|
}
|
|
1834
2709
|
|
|
2710
|
+
if (bindNode.dataset.bind === 'copy-column-format-field') {
|
|
2711
|
+
updateCopyColumnModalFormatField(
|
|
2712
|
+
bindNode.dataset.field,
|
|
2713
|
+
bindNode instanceof HTMLInputElement && bindNode.type === 'checkbox' ? bindNode.checked : bindNode.value,
|
|
2714
|
+
);
|
|
2715
|
+
return;
|
|
2716
|
+
}
|
|
2717
|
+
|
|
1835
2718
|
if (bindNode.dataset.bind === 'current-query') {
|
|
1836
2719
|
invalidateMainRenderCache();
|
|
1837
2720
|
syncQueryEditorHighlight(bindNode);
|
|
@@ -1840,6 +2723,12 @@ document.addEventListener('input', event => {
|
|
|
1840
2723
|
return;
|
|
1841
2724
|
}
|
|
1842
2725
|
|
|
2726
|
+
if (bindNode.dataset.bind === 'document-field') {
|
|
2727
|
+
updateCurrentDocumentDraftField(bindNode.dataset.field, bindNode.value);
|
|
2728
|
+
scheduleDocumentAutosave(getState().documents.selectedId);
|
|
2729
|
+
return;
|
|
2730
|
+
}
|
|
2731
|
+
|
|
1843
2732
|
if (bindNode.dataset.bind === 'data-search-query') {
|
|
1844
2733
|
void setDataSearchQuery(bindNode.value);
|
|
1845
2734
|
return;
|
|
@@ -1873,7 +2762,12 @@ document.addEventListener('input', event => {
|
|
|
1873
2762
|
return;
|
|
1874
2763
|
}
|
|
1875
2764
|
|
|
1876
|
-
updateCurrentTableDesignerField(bindNode.dataset.field, bindNode.value);
|
|
2765
|
+
updateCurrentTableDesignerField(bindNode.dataset.field, bindNode.value, { notify: false });
|
|
2766
|
+
|
|
2767
|
+
if (!syncTableDesignerDraftUi(bindNode)) {
|
|
2768
|
+
renderApp(getState());
|
|
2769
|
+
}
|
|
2770
|
+
|
|
1877
2771
|
return;
|
|
1878
2772
|
}
|
|
1879
2773
|
|
|
@@ -1882,7 +2776,14 @@ document.addEventListener('input', event => {
|
|
|
1882
2776
|
return;
|
|
1883
2777
|
}
|
|
1884
2778
|
|
|
1885
|
-
updateCurrentTableDesignerColumnField(bindNode.dataset.columnId, bindNode.dataset.field, bindNode.value
|
|
2779
|
+
updateCurrentTableDesignerColumnField(bindNode.dataset.columnId, bindNode.dataset.field, bindNode.value, {
|
|
2780
|
+
notify: false,
|
|
2781
|
+
});
|
|
2782
|
+
|
|
2783
|
+
if (!syncTableDesignerDraftUi(bindNode)) {
|
|
2784
|
+
renderApp(getState());
|
|
2785
|
+
}
|
|
2786
|
+
|
|
1886
2787
|
return;
|
|
1887
2788
|
}
|
|
1888
2789
|
|
|
@@ -1943,6 +2844,19 @@ document.addEventListener(
|
|
|
1943
2844
|
);
|
|
1944
2845
|
|
|
1945
2846
|
document.addEventListener('change', event => {
|
|
2847
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
2848
|
+
const timestampInput = target?.closest('[data-row-editor-timestamp-source]');
|
|
2849
|
+
const textCellInput = target?.closest('[data-row-editor-text-source]');
|
|
2850
|
+
|
|
2851
|
+
if (timestampInput) {
|
|
2852
|
+
syncRowEditorTimestampPreview(timestampInput);
|
|
2853
|
+
syncRowEditorFilePathPreview(timestampInput);
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
if (textCellInput) {
|
|
2857
|
+
syncRowEditorCharacterCount(textCellInput);
|
|
2858
|
+
}
|
|
2859
|
+
|
|
1946
2860
|
const bindNode = event.target.closest('[data-bind]');
|
|
1947
2861
|
|
|
1948
2862
|
if (!bindNode) {
|
|
@@ -1985,6 +2899,16 @@ document.addEventListener('change', event => {
|
|
|
1985
2899
|
return;
|
|
1986
2900
|
}
|
|
1987
2901
|
|
|
2902
|
+
if (bindNode.dataset.bind === 'document-import-file') {
|
|
2903
|
+
void handleDocumentMarkdownImport(bindNode);
|
|
2904
|
+
return;
|
|
2905
|
+
}
|
|
2906
|
+
|
|
2907
|
+
if (bindNode.dataset.bind === 'document-insert-query-select') {
|
|
2908
|
+
updateDocumentInsertQuerySelection(bindNode.value);
|
|
2909
|
+
return;
|
|
2910
|
+
}
|
|
2911
|
+
|
|
1988
2912
|
if (bindNode.dataset.bind === 'table-designer-field') {
|
|
1989
2913
|
if (bindNode instanceof HTMLInputElement && bindNode.type === 'checkbox') {
|
|
1990
2914
|
updateCurrentTableDesignerField(bindNode.dataset.field, bindNode.checked);
|
|
@@ -2001,7 +2925,12 @@ document.addEventListener('change', event => {
|
|
|
2001
2925
|
return;
|
|
2002
2926
|
}
|
|
2003
2927
|
|
|
2004
|
-
updateCurrentTableDesignerField(bindNode.dataset.field, bindNode.value);
|
|
2928
|
+
updateCurrentTableDesignerField(bindNode.dataset.field, bindNode.value, { notify: false });
|
|
2929
|
+
|
|
2930
|
+
if (!syncTableDesignerDraftUi(bindNode)) {
|
|
2931
|
+
renderApp(getState());
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2005
2934
|
return;
|
|
2006
2935
|
}
|
|
2007
2936
|
|
|
@@ -2018,7 +2947,14 @@ document.addEventListener('change', event => {
|
|
|
2018
2947
|
return;
|
|
2019
2948
|
}
|
|
2020
2949
|
|
|
2021
|
-
updateCurrentTableDesignerColumnField(bindNode.dataset.columnId, bindNode.dataset.field, bindNode.value
|
|
2950
|
+
updateCurrentTableDesignerColumnField(bindNode.dataset.columnId, bindNode.dataset.field, bindNode.value, {
|
|
2951
|
+
notify: false,
|
|
2952
|
+
});
|
|
2953
|
+
|
|
2954
|
+
if (!syncTableDesignerDraftUi(bindNode)) {
|
|
2955
|
+
renderApp(getState());
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2022
2958
|
return;
|
|
2023
2959
|
}
|
|
2024
2960
|
|
|
@@ -2027,6 +2963,16 @@ document.addEventListener('change', event => {
|
|
|
2027
2963
|
return;
|
|
2028
2964
|
}
|
|
2029
2965
|
|
|
2966
|
+
if (bindNode.dataset.bind === 'table-designer-constraint-field') {
|
|
2967
|
+
updateCurrentTableDesignerConstraintField(
|
|
2968
|
+
bindNode.dataset.constraintKind,
|
|
2969
|
+
bindNode.dataset.constraintId,
|
|
2970
|
+
bindNode.dataset.field,
|
|
2971
|
+
bindNode.value,
|
|
2972
|
+
);
|
|
2973
|
+
return;
|
|
2974
|
+
}
|
|
2975
|
+
|
|
2030
2976
|
if (bindNode.dataset.bind === 'query-chart-draft:chartType') {
|
|
2031
2977
|
updateCurrentQueryChartDraftField('chartType', bindNode.value);
|
|
2032
2978
|
return;
|
|
@@ -2057,7 +3003,8 @@ document.addEventListener('submit', async event => {
|
|
|
2057
3003
|
}
|
|
2058
3004
|
|
|
2059
3005
|
event.preventDefault();
|
|
2060
|
-
const
|
|
3006
|
+
const submitter = event.submitter instanceof HTMLElement ? event.submitter : null;
|
|
3007
|
+
const formData = submitter ? new FormData(form, submitter) : new FormData(form);
|
|
2061
3008
|
|
|
2062
3009
|
switch (form.dataset.form) {
|
|
2063
3010
|
case 'open-connection': {
|
|
@@ -2140,6 +3087,32 @@ document.addEventListener('submit', async event => {
|
|
|
2140
3087
|
case 'delete-query-history-confirm':
|
|
2141
3088
|
await submitDeleteQueryHistoryConfirmation();
|
|
2142
3089
|
return;
|
|
3090
|
+
case 'delete-document-confirm': {
|
|
3091
|
+
const result = await submitDeleteDocumentConfirmation();
|
|
3092
|
+
|
|
3093
|
+
if (result.deleted) {
|
|
3094
|
+
router.navigate(
|
|
3095
|
+
result.nextDocumentId ? `/documents/${encodeURIComponent(result.nextDocumentId)}` : '/documents',
|
|
3096
|
+
);
|
|
3097
|
+
}
|
|
3098
|
+
return;
|
|
3099
|
+
}
|
|
3100
|
+
case 'document-insert-table': {
|
|
3101
|
+
const inserted = await submitDocumentInsertTable();
|
|
3102
|
+
|
|
3103
|
+
if (inserted) {
|
|
3104
|
+
scheduleDocumentAutosave(getState().documents.selectedId);
|
|
3105
|
+
}
|
|
3106
|
+
return;
|
|
3107
|
+
}
|
|
3108
|
+
case 'document-insert-note': {
|
|
3109
|
+
const inserted = submitDocumentInsertNote();
|
|
3110
|
+
|
|
3111
|
+
if (inserted) {
|
|
3112
|
+
scheduleDocumentAutosave(getState().documents.selectedId);
|
|
3113
|
+
}
|
|
3114
|
+
return;
|
|
3115
|
+
}
|
|
2143
3116
|
case 'apply-row-update-preview':
|
|
2144
3117
|
await submitRowUpdatePreviewConfirmation();
|
|
2145
3118
|
return;
|
|
@@ -2149,6 +3122,9 @@ document.addEventListener('submit', async event => {
|
|
|
2149
3122
|
case 'create-media-tagging-mapping-table':
|
|
2150
3123
|
await submitCreateMediaTaggingMappingTable();
|
|
2151
3124
|
return;
|
|
3125
|
+
case 'copy-column':
|
|
3126
|
+
await submitCopyColumnModal(formData);
|
|
3127
|
+
return;
|
|
2152
3128
|
case 'save-data-row': {
|
|
2153
3129
|
const values = {};
|
|
2154
3130
|
|