sqlite-hub 0.16.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.
@@ -23,6 +23,7 @@
23
23
  <script src="/vendor/echarts/dist/echarts.min.js"></script>
24
24
  <script src="/vendor/elkjs/lib/elk.bundled.js"></script>
25
25
  <script src="/vendor/cytoscape-elk/dist/cytoscape-elk.js"></script>
26
+ <script src="/vendor/marked/lib/marked.umd.js"></script>
26
27
  <script src="js/app.js" type="module"></script>
27
28
  </body>
28
29
  </html>
@@ -490,3 +490,31 @@ export function downloadTableExport(tableName, options = {}) {
490
490
  export function downloadTableCsv(tableName, options = {}) {
491
491
  return downloadTableExport(tableName, { ...options, format: "csv" });
492
492
  }
493
+
494
+ export function getDocuments() {
495
+ return request("/api/documents");
496
+ }
497
+
498
+ export function createDocument(payload = {}) {
499
+ return request("/api/documents", {
500
+ method: "POST",
501
+ body: payload,
502
+ });
503
+ }
504
+
505
+ export function getDocument(documentId) {
506
+ return request(`/api/documents/${encodeURIComponent(documentId)}`);
507
+ }
508
+
509
+ export function updateDocument(documentId, payload = {}) {
510
+ return request(`/api/documents/${encodeURIComponent(documentId)}`, {
511
+ method: "PATCH",
512
+ body: payload,
513
+ });
514
+ }
515
+
516
+ export function deleteDocument(documentId) {
517
+ return request(`/api/documents/${encodeURIComponent(documentId)}`, {
518
+ method: "DELETE",
519
+ });
520
+ }
@@ -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,12 +50,15 @@ import {
48
50
  openDeleteQueryHistoryModal,
49
51
  openDeleteQueryChartModal,
50
52
  openDataExportModal,
53
+ openDeleteDocumentModal,
51
54
  openQueryExportModal,
52
55
  openDataRowByIdentity,
53
56
  openEditConnectionModal,
54
57
  openCreateQueryChartModal,
55
58
  openCopyColumnModal,
56
59
  openEditQueryChartModal,
60
+ openDocumentInsertNoteModal,
61
+ openDocumentInsertTableModal,
57
62
  preserveCurrentDataRowSelectionForReload,
58
63
  openDataRowUpdatePreview,
59
64
  openEditorRowUpdatePreview,
@@ -66,6 +71,7 @@ import {
66
71
  runQueryHistoryItem,
67
72
  skipCurrentMediaTaggingItem,
68
73
  saveCurrentQueryChartDraft,
74
+ saveCurrentDocument,
69
75
  saveCurrentMediaTaggingConfig,
70
76
  selectDataRow,
71
77
  selectEditorRow,
@@ -90,6 +96,9 @@ import {
90
96
  setEditorPanelVisibility,
91
97
  setEditorTab,
92
98
  submitDeleteChartConfirmation,
99
+ submitDeleteDocumentConfirmation,
100
+ submitDocumentInsertNote,
101
+ submitDocumentInsertTable,
93
102
  submitCreateMediaTaggingTagTable,
94
103
  submitCreateMediaTaggingMappingTable,
95
104
  submitDeleteQueryHistoryConfirmation,
@@ -100,6 +109,7 @@ import {
100
109
  setQueryHistorySearchInput,
101
110
  setQueryHistoryTab,
102
111
  setCopyColumnModalError,
112
+ setCopyColumnModalEditedText,
103
113
  setCopyColumnModalSubmitting,
104
114
  setRoute,
105
115
  saveQueryHistoryNotes,
@@ -107,6 +117,8 @@ import {
107
117
  saveCurrentTableDesignerDraft,
108
118
  toggleChartsResultsPanel,
109
119
  toggleChartsSqlPanel,
120
+ toggleCurrentDocumentTodo,
121
+ toggleDocumentsPane,
110
122
  setMediaTaggingWorkflowMediaDetailsVisible,
111
123
  setMediaTaggingWorkflowMediaRotationDegrees,
112
124
  queueTableDesignerCsvImport,
@@ -124,6 +136,8 @@ import {
124
136
  updateCurrentMediaTaggingField,
125
137
  updateCurrentMediaTaggingTagFormField,
126
138
  updateCopyColumnModalFormatField,
139
+ updateCurrentDocumentDraftField,
140
+ updateDocumentInsertQuerySelection,
127
141
  updateCurrentQueryChartDraftConfigField,
128
142
  updateCurrentQueryChartDraftField,
129
143
  updateCurrentTableDesignerColumnField,
@@ -136,6 +150,7 @@ import {
136
150
  import { renderChartsDetail, renderChartsView } from './views/charts.js';
137
151
  import { renderConnectionsView } from './views/connections.js';
138
152
  import { renderDataRowEditorPanel, renderDataView } from './views/data.js';
153
+ import { renderDocumentsView } from './views/documents.js';
139
154
  import { renderEditorView } from './views/editor.js';
140
155
  import { renderLandingView } from './views/landing.js';
141
156
  import { renderMediaTaggingView } from './views/mediaTagging.js';
@@ -197,6 +212,10 @@ let lastRenderedLockedRoute = false;
197
212
  let pendingNewTableDesignerAutofocus = false;
198
213
  let pendingQueryEditorFocus = false;
199
214
  let pendingMediaTaggingTagSearchFocus = false;
215
+ let documentAutosaveTimer = null;
216
+ let pendingDocumentAutosaveId = null;
217
+
218
+ const DOCUMENT_AUTOSAVE_DELAY_MS = 5000;
200
219
 
201
220
  const APP_TITLE = 'SQLite Hub';
202
221
  const ROUTE_TITLE_SEGMENTS = {
@@ -207,6 +226,7 @@ const ROUTE_TITLE_SEGMENTS = {
207
226
  editor: 'SQL Editor',
208
227
  editorResults: 'SQL Editor',
209
228
  charts: 'Charts',
229
+ documents: 'Documents',
210
230
  tableDesigner: 'Table Designer',
211
231
  mediaTaggingSetup: 'Media Tagging',
212
232
  mediaTaggingQueue: 'Tagging Queue',
@@ -797,13 +817,58 @@ function readFileAsText(file) {
797
817
  resolve(String(reader.result ?? ''));
798
818
  };
799
819
  reader.onerror = () => {
800
- reject(reader.error ?? new Error('The selected CSV file could not be read.'));
820
+ reject(reader.error ?? new Error('The selected file could not be read.'));
801
821
  };
802
822
 
803
823
  reader.readAsText(file);
804
824
  });
805
825
  }
806
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
+
807
872
  async function buildConnectionLogoUpload(file) {
808
873
  if (!(file instanceof File) || !file.size) {
809
874
  return null;
@@ -852,6 +917,8 @@ function resolveView(state) {
852
917
  return renderOverviewView(state);
853
918
  case 'charts':
854
919
  return renderChartsView(state);
920
+ case 'documents':
921
+ return renderDocumentsView(state);
855
922
  case 'data':
856
923
  return renderDataView(state);
857
924
  case 'editor':
@@ -1101,6 +1168,39 @@ async function handleTableDesignerCsvImport(fileInput) {
1101
1168
  }
1102
1169
  }
1103
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
+
1104
1204
  function renderApp(state) {
1105
1205
  syncDocumentTitle(state);
1106
1206
 
@@ -1118,6 +1218,7 @@ function renderApp(state) {
1118
1218
  'editorResults',
1119
1219
  'data',
1120
1220
  'charts',
1221
+ 'documents',
1121
1222
  'structure',
1122
1223
  'tableDesigner',
1123
1224
  'mediaTaggingSetup',
@@ -1520,6 +1621,69 @@ function buildCopyColumnExportFilename(columnName, copyMode) {
1520
1621
  return `${columnSlug}-${metadata.suffix}.${metadata.extension}`;
1521
1622
  }
1522
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
+
1523
1687
  function downloadTextFile({ text, filename, mimeType }) {
1524
1688
  const blob = new Blob([text], { type: mimeType });
1525
1689
  const url = URL.createObjectURL(blob);
@@ -1677,6 +1841,37 @@ function exportRowEditorJson() {
1677
1841
  showToast(`Row from "${payload.label}" exported as JSON.`, 'success');
1678
1842
  }
1679
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
+
1680
1875
  function preserveDataRowEditorSelectionForReload() {
1681
1876
  preserveCurrentDataRowSelectionForReload();
1682
1877
  }
@@ -1698,6 +1893,9 @@ async function submitCopyColumnModal(formData) {
1698
1893
  const outputSeparator = lineBreaks ? '\n' : separator;
1699
1894
  const intent = String(formData.get('intent') ?? 'copy');
1700
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;
1701
1899
  const result = getCopyColumnResult(state, scope);
1702
1900
  const hasColumn = (result?.columns ?? []).some(column => String(column) === columnName);
1703
1901
 
@@ -1710,20 +1908,57 @@ async function submitCopyColumnModal(formData) {
1710
1908
  return;
1711
1909
  }
1712
1910
 
1713
- if (!isMarkdownTodoCopyColumnMode(copyMode)) {
1911
+ if (!isMarkdownTodo) {
1714
1912
  storeCopyColumnPreferences({ separator, wrapper, lineBreaks });
1913
+ } else if (editedText !== null) {
1914
+ setCopyColumnModalEditedText(editedText);
1715
1915
  }
1716
1916
  setCopyColumnModalSubmitting(true);
1717
1917
 
1718
- const { text, valueCount } = buildCopyColumnText({
1918
+ const generatedOutput = buildCopyColumnText({
1719
1919
  result,
1720
1920
  columnName,
1721
1921
  copyMode,
1722
1922
  separator: outputSeparator,
1723
1923
  wrapper,
1724
1924
  });
1925
+ const text = isMarkdownTodo && editedText !== null ? editedText : generatedOutput.text;
1926
+ const valueCount = isMarkdownTodo && editedText !== null
1927
+ ? countEditedMarkdownTodoItems(editedText)
1928
+ : generatedOutput.valueCount;
1725
1929
 
1726
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
+
1727
1962
  if (isExportIntent) {
1728
1963
  const metadata = getCopyColumnExportMetadata(copyMode);
1729
1964
 
@@ -1754,10 +1989,23 @@ async function submitCopyColumnModal(formData) {
1754
1989
  );
1755
1990
  } catch (error) {
1756
1991
  setCopyColumnModalError({
1757
- code: isExportIntent ? 'COPY_COLUMN_EXPORT_FAILED' : 'CLIPBOARD_ACCESS_FAILED',
1758
- message: error?.message || (isExportIntent ? 'Column export failed.' : 'Clipboard access failed.'),
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.'),
1759
2004
  });
1760
- showToast(isExportIntent ? 'Column export failed.' : 'Clipboard access failed.', 'alert');
2005
+ showToast(
2006
+ isDocumentIntent ? 'Document export failed.' : isExportIntent ? 'Column export failed.' : 'Clipboard access failed.',
2007
+ 'alert',
2008
+ );
1761
2009
  }
1762
2010
  }
1763
2011
 
@@ -1794,6 +2042,58 @@ async function handleAction(actionNode) {
1794
2042
  mode: actionNode.dataset.copyMode,
1795
2043
  });
1796
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);
2096
+ return;
1797
2097
  case 'open-create-query-chart-modal':
1798
2098
  openCreateQueryChartModal();
1799
2099
  return;
@@ -2284,6 +2584,20 @@ document.addEventListener('keydown', event => {
2284
2584
  const target = event.target;
2285
2585
  const state = getState();
2286
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
+
2287
2601
  // Handle Enter key in tag form fields to trigger create tag
2288
2602
  if (
2289
2603
  event.key === 'Enter' &&
@@ -2409,6 +2723,12 @@ document.addEventListener('input', event => {
2409
2723
  return;
2410
2724
  }
2411
2725
 
2726
+ if (bindNode.dataset.bind === 'document-field') {
2727
+ updateCurrentDocumentDraftField(bindNode.dataset.field, bindNode.value);
2728
+ scheduleDocumentAutosave(getState().documents.selectedId);
2729
+ return;
2730
+ }
2731
+
2412
2732
  if (bindNode.dataset.bind === 'data-search-query') {
2413
2733
  void setDataSearchQuery(bindNode.value);
2414
2734
  return;
@@ -2579,6 +2899,16 @@ document.addEventListener('change', event => {
2579
2899
  return;
2580
2900
  }
2581
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
+
2582
2912
  if (bindNode.dataset.bind === 'table-designer-field') {
2583
2913
  if (bindNode instanceof HTMLInputElement && bindNode.type === 'checkbox') {
2584
2914
  updateCurrentTableDesignerField(bindNode.dataset.field, bindNode.checked);
@@ -2757,6 +3087,32 @@ document.addEventListener('submit', async event => {
2757
3087
  case 'delete-query-history-confirm':
2758
3088
  await submitDeleteQueryHistoryConfirmation();
2759
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
+ }
2760
3116
  case 'apply-row-update-preview':
2761
3117
  await submitRowUpdatePreviewConfirmation();
2762
3118
  return;