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.
@@ -1,5 +1,6 @@
1
1
  import { escapeHtml, formatNumber, highlightSql, truncateMiddle } from "../utils/format.js";
2
2
  import {
3
+ buildCopyColumnText,
3
4
  buildCopyColumnPreviewText,
4
5
  getCopyColumnActionLabel,
5
6
  getCopyColumnExportMetadata,
@@ -170,7 +171,7 @@ function renderOpenConnectionForm(modal) {
170
171
  text: "Open read-only",
171
172
  })}
172
173
  ${renderError(modal.error)}
173
- <div class="flex items-center justify-end gap-3 pt-2">
174
+ <div class="flex items-center justify-between gap-3 pt-2">
174
175
  <button
175
176
  class="standard-button"
176
177
  data-action="close-modal"
@@ -244,7 +245,7 @@ function renderEditConnectionForm(modal) {
244
245
  text: "Open read-only",
245
246
  })}
246
247
  ${renderError(modal.error)}
247
- <div class="flex items-center justify-end gap-3 pt-2">
248
+ <div class="flex items-center justify-between gap-3 pt-2">
248
249
  <button
249
250
  class="standard-button"
250
251
  data-action="close-modal"
@@ -277,7 +278,7 @@ function renderCreateDatabaseForm(modal) {
277
278
  placeholder: "Optional display name",
278
279
  })}
279
280
  ${renderError(modal.error)}
280
- <div class="flex items-center justify-end gap-3 pt-2">
281
+ <div class="flex items-center justify-between gap-3 pt-2">
281
282
  <button
282
283
  class="standard-button"
283
284
  data-action="close-modal"
@@ -363,7 +364,7 @@ function renderImportSqlForm(modal, state) {
363
364
  explicit path instead of file upload.
364
365
  </p>
365
366
  ${renderError(modal.error)}
366
- <div class="flex items-center justify-end gap-3 pt-2">
367
+ <div class="flex items-center justify-between gap-3 pt-2">
367
368
  <button
368
369
  class="standard-button"
369
370
  data-action="close-modal"
@@ -423,7 +424,7 @@ function renderDeleteRowConfirmForm(modal) {
423
424
  rowPreviewMarkup,
424
425
  "</div>",
425
426
  renderError(modal.error),
426
- '<div class="flex items-center justify-end gap-3 pt-2">',
427
+ '<div class="flex items-center justify-between gap-3 pt-2">',
427
428
  '<button class="standard-button" data-action="close-modal" type="button">Cancel</button>',
428
429
  '<button class="delete-button" type="submit">',
429
430
  modal.submitting ? "Deleting..." : "Delete Row",
@@ -513,7 +514,7 @@ function renderRowUpdatePreviewForm(modal) {
513
514
  </div>
514
515
  ${paramsMarkup}
515
516
  ${renderError(modal.error)}
516
- <div class="flex items-center justify-end gap-3 pt-2">
517
+ <div class="flex items-center justify-between gap-3 pt-2">
517
518
  <button class="standard-button" data-action="close-modal" type="button">Cancel</button>
518
519
  <button class="signature-button" type="submit">
519
520
  ${modal.submitting ? 'Applying...' : 'Apply Changes'}
@@ -753,7 +754,7 @@ function renderChartEditorForm(modal, state) {
753
754
  : ""
754
755
  }
755
756
  ${renderError(modal.error)}
756
- <div class="flex items-center justify-end gap-3 pt-2">
757
+ <div class="flex items-center justify-between gap-3 pt-2">
757
758
  <button
758
759
  class="standard-button"
759
760
  data-action="close-modal"
@@ -781,7 +782,7 @@ function renderDeleteChartForm(modal) {
781
782
  '<p class="text-sm leading-7 text-on-surface-variant/65">The linked query-history entry stays intact. Only this chart definition is removed.</p>',
782
783
  "</div>",
783
784
  renderError(modal.error),
784
- '<div class="flex items-center justify-end gap-3 pt-2">',
785
+ '<div class="flex items-center justify-between gap-3 pt-2">',
785
786
  '<button class="standard-button" data-action="close-modal" type="button">Cancel</button>',
786
787
  '<button class="delete-button" type="submit">',
787
788
  modal.submitting ? "Deleting..." : "Delete Chart",
@@ -798,7 +799,7 @@ function renderDeleteQueryHistoryForm(modal) {
798
799
  '<p class="text-sm leading-7 text-on-surface-variant/65">This removes the query-history entry and all recorded runs linked to it.</p>',
799
800
  "</div>",
800
801
  renderError(modal.error),
801
- '<div class="flex items-center justify-end gap-3 pt-2">',
802
+ '<div class="flex items-center justify-between gap-3 pt-2">',
802
803
  '<button class="standard-button" data-action="close-modal" type="button">Cancel</button>',
803
804
  '<button class="delete-button" type="submit">',
804
805
  modal.submitting ? "Deleting..." : "Delete Query",
@@ -806,6 +807,147 @@ function renderDeleteQueryHistoryForm(modal) {
806
807
  ].join("");
807
808
  }
808
809
 
810
+ function renderDeleteDocumentForm(modal) {
811
+ return [
812
+ '<form class="space-y-5" data-form="delete-document-confirm"><div class="space-y-3">',
813
+ '<p class="text-sm leading-7 text-on-surface">Delete document <span class="font-bold text-primary-container">',
814
+ escapeHtml(modal.filename ?? "document"),
815
+ "</span>?</p>",
816
+ '<p class="text-sm leading-7 text-on-surface-variant/65">This removes the Markdown document from the active database document folder.</p>',
817
+ '<div class="border border-outline-variant/10 bg-surface-container-lowest px-4 py-3 text-[10px] font-mono uppercase tracking-[0.18em] text-on-surface-variant/55">',
818
+ formatNumber(modal.contentLength ?? 0),
819
+ " chars</div>",
820
+ "</div>",
821
+ renderError(modal.error),
822
+ '<div class="flex items-center justify-between gap-3 pt-2">',
823
+ '<button class="standard-button" data-action="close-modal" type="button">Cancel</button>',
824
+ '<button class="delete-button" type="submit">',
825
+ modal.submitting ? "Deleting..." : "Delete Document",
826
+ "</button></div></form>",
827
+ ].join("");
828
+ }
829
+
830
+ function getDocumentInsertQueryTitle(query) {
831
+ return query?.displayTitle || query?.title || query?.previewSql || query?.rawSql || "Saved query";
832
+ }
833
+
834
+ function getSelectedDocumentInsertQuery(modal) {
835
+ const selectedHistoryId = String(modal.selectedHistoryId ?? "");
836
+
837
+ return (modal.queries ?? []).find((query) => String(query.id) === selectedHistoryId) ?? null;
838
+ }
839
+
840
+ function renderDocumentInsertQuerySelect(modal, emptyText) {
841
+ const queries = modal.queries ?? [];
842
+
843
+ if (modal.loading) {
844
+ return '<div class="border border-outline-variant/10 bg-surface-container-lowest px-4 py-3 text-sm text-on-surface-variant/65">Loading saved queries...</div>';
845
+ }
846
+
847
+ if (!queries.length) {
848
+ return `<div class="border border-outline-variant/10 bg-surface-container-lowest px-4 py-3 text-sm text-on-surface-variant/65">${escapeHtml(emptyText)}</div>`;
849
+ }
850
+
851
+ return `
852
+ <label class="block space-y-2">
853
+ <span class="text-[10px] font-mono uppercase tracking-[0.22em] text-on-surface-variant/60">
854
+ Saved Query
855
+ </span>
856
+ <select
857
+ class="control-select w-full border border-outline-variant/20 bg-surface-container-lowest text-sm text-on-surface outline-none transition-colors focus:border-primary-container"
858
+ data-bind="document-insert-query-select"
859
+ name="historyId"
860
+ >
861
+ ${queries
862
+ .map(
863
+ (query) => `
864
+ <option
865
+ value="${escapeHtml(query.id)}"
866
+ ${String(query.id) === String(modal.selectedHistoryId) ? "selected" : ""}
867
+ >
868
+ ${escapeHtml(getDocumentInsertQueryTitle(query))}
869
+ </option>
870
+ `
871
+ )
872
+ .join("")}
873
+ </select>
874
+ </label>
875
+ `;
876
+ }
877
+
878
+ function renderDocumentInsertQueryPreview(query) {
879
+ if (!query) {
880
+ return "";
881
+ }
882
+
883
+ return `
884
+ <div class="space-y-2">
885
+ <div class="text-[10px] font-mono uppercase tracking-[0.22em] text-on-surface-variant/60">
886
+ Query Preview
887
+ </div>
888
+ <pre class="max-h-44 overflow-auto border border-outline-variant/10 bg-surface-container-lowest px-4 py-3 font-mono text-xs leading-6 text-on-surface-variant/75 custom-scrollbar">${escapeHtml(
889
+ query.rawSql || query.previewSql || ""
890
+ )}</pre>
891
+ </div>
892
+ `;
893
+ }
894
+
895
+ function renderDocumentInsertTableForm(modal) {
896
+ const selectedQuery = getSelectedDocumentInsertQuery(modal);
897
+ const disabledAttribute = modal.loading || modal.submitting || !(modal.queries ?? []).length
898
+ ? 'disabled aria-disabled="true"'
899
+ : "";
900
+
901
+ return `
902
+ <form class="space-y-5" data-form="document-insert-table">
903
+ ${renderDocumentInsertQuerySelect(modal, "No saved queries are available for this database.")}
904
+ ${renderDocumentInsertQueryPreview(selectedQuery)}
905
+ ${renderError(modal.error)}
906
+ <div class="flex items-center justify-between gap-3 pt-2">
907
+ <button class="standard-button" data-action="close-modal" type="button">Cancel</button>
908
+ <button class="signature-button" type="submit" ${disabledAttribute}>
909
+ ${modal.submitting ? "Inserting..." : "Insert Table"}
910
+ </button>
911
+ </div>
912
+ </form>
913
+ `;
914
+ }
915
+
916
+ function renderDocumentInsertNoteForm(modal) {
917
+ const selectedQuery = getSelectedDocumentInsertQuery(modal);
918
+ const note = String(selectedQuery?.notes ?? "").trim();
919
+ const disabledAttribute = modal.loading || modal.submitting || !(modal.queries ?? []).length
920
+ ? 'disabled aria-disabled="true"'
921
+ : "";
922
+
923
+ return `
924
+ <form class="space-y-5" data-form="document-insert-note">
925
+ ${renderDocumentInsertQuerySelect(modal, "No saved queries with notes are available for this database.")}
926
+ ${
927
+ note
928
+ ? `
929
+ <div class="space-y-2">
930
+ <div class="text-[10px] font-mono uppercase tracking-[0.22em] text-on-surface-variant/60">
931
+ Note Preview
932
+ </div>
933
+ <pre class="max-h-64 overflow-auto whitespace-pre-wrap border border-outline-variant/10 bg-surface-container-lowest px-4 py-3 text-sm leading-6 text-on-surface custom-scrollbar">${escapeHtml(
934
+ note
935
+ )}</pre>
936
+ </div>
937
+ `
938
+ : ""
939
+ }
940
+ ${renderError(modal.error)}
941
+ <div class="flex items-center justify-between gap-3 pt-2">
942
+ <button class="standard-button" data-action="close-modal" type="button">Cancel</button>
943
+ <button class="signature-button" type="submit" ${disabledAttribute}>
944
+ ${modal.submitting ? "Inserting..." : "Insert Note"}
945
+ </button>
946
+ </div>
947
+ </form>
948
+ `;
949
+ }
950
+
809
951
  function renderQueryExportPreview(lines = []) {
810
952
  return lines.map((line) => `<span class="block whitespace-pre">${escapeHtml(line)}</span>`).join("");
811
953
  }
@@ -888,7 +1030,7 @@ function renderTextExportModal(modal, action) {
888
1030
  .join("")}
889
1031
  </div>
890
1032
  ${renderError(modal.error)}
891
- <div class="flex justify-end">
1033
+ <div class="flex justify-start">
892
1034
  <button class="standard-button" data-action="close-modal" type="button">
893
1035
  Cancel
894
1036
  </button>
@@ -911,23 +1053,47 @@ function getCopyColumnResult(state, modal) {
911
1053
 
912
1054
  function renderCopyColumnPreview(modal, state) {
913
1055
  const result = getCopyColumnResult(state, modal);
1056
+ const isMarkdownTodo = isMarkdownTodoCopyColumnMode(modal.copyMode);
914
1057
  const separator = Boolean(modal.lineBreaks) && !isMarkdownTodoCopyColumnMode(modal.copyMode)
915
1058
  ? "\n"
916
1059
  : String(modal.separator ?? ",");
917
1060
  const wrapper = String(modal.wrapper ?? '"');
918
- const preview = buildCopyColumnPreviewText({
919
- result,
920
- columnName: modal.columnName,
921
- copyMode: modal.copyMode,
922
- separator,
923
- wrapper,
924
- maxRows: isMarkdownTodoCopyColumnMode(modal.copyMode) ? 10 : 4,
925
- });
926
-
927
- if (!preview) {
1061
+ const preview = isMarkdownTodo
1062
+ ? modal.editedText ?? buildCopyColumnText({
1063
+ result,
1064
+ columnName: modal.columnName,
1065
+ copyMode: modal.copyMode,
1066
+ separator: "\n",
1067
+ wrapper,
1068
+ }).text
1069
+ : buildCopyColumnPreviewText({
1070
+ result,
1071
+ columnName: modal.columnName,
1072
+ copyMode: modal.copyMode,
1073
+ separator,
1074
+ wrapper,
1075
+ maxRows: 4,
1076
+ });
1077
+
1078
+ if (!preview && !isMarkdownTodo) {
928
1079
  return "";
929
1080
  }
930
1081
 
1082
+ if (isMarkdownTodo) {
1083
+ return `
1084
+ <label class="block space-y-2">
1085
+ <span class="text-[10px] font-mono uppercase tracking-[0.22em] text-on-surface-variant/60">
1086
+ Editable Preview
1087
+ </span>
1088
+ <textarea
1089
+ class="copy-column-preview copy-column-preview--editable custom-scrollbar"
1090
+ name="editedText"
1091
+ spellcheck="true"
1092
+ >${escapeHtml(preview)}</textarea>
1093
+ </label>
1094
+ `;
1095
+ }
1096
+
931
1097
  return `
932
1098
  <div class="space-y-2">
933
1099
  <div class="text-[10px] font-mono uppercase tracking-[0.22em] text-on-surface-variant/60">
@@ -977,7 +1143,7 @@ function renderCopyColumnFormatField({ label, name, value = "" }) {
977
1143
  `;
978
1144
  }
979
1145
 
980
- function renderCopyColumnModal(modal, state) {
1146
+ export function renderCopyColumnModal(modal, state) {
981
1147
  const result = getCopyColumnResult(state, modal);
982
1148
  const rows = result?.rows ?? [];
983
1149
  const valueCount = modal.copyMode === "first-10" ? Math.min(rows.length, 10) : rows.length;
@@ -1029,7 +1195,7 @@ function renderCopyColumnModal(modal, state) {
1029
1195
  ${formatFieldsMarkup}
1030
1196
  ${renderCopyColumnPreview(modal, state)}
1031
1197
  ${renderError(modal.error)}
1032
- <div class="flex flex-wrap items-center justify-end gap-3 pt-2">
1198
+ <div class="flex flex-wrap items-center justify-between gap-3 pt-2">
1033
1199
  <button
1034
1200
  class="standard-button"
1035
1201
  data-action="close-modal"
@@ -1038,24 +1204,41 @@ function renderCopyColumnModal(modal, state) {
1038
1204
  >
1039
1205
  Cancel
1040
1206
  </button>
1041
- <button
1042
- class="standard-button"
1043
- name="intent"
1044
- type="submit"
1045
- value="export"
1046
- ${disabledAttribute}
1047
- >
1048
- ${modal.submitting ? "Working..." : `Export as ${exportMetadata.extension.toUpperCase()}`}
1049
- </button>
1050
- <button
1051
- class="signature-button"
1052
- name="intent"
1053
- type="submit"
1054
- value="copy"
1055
- ${disabledAttribute}
1056
- >
1057
- ${modal.submitting ? "Working..." : "Copy"}
1058
- </button>
1207
+ <div class="flex flex-wrap items-center justify-end gap-3">
1208
+ <button
1209
+ class="standard-button"
1210
+ name="intent"
1211
+ type="submit"
1212
+ value="export"
1213
+ ${disabledAttribute}
1214
+ >
1215
+ ${modal.submitting ? "Working..." : `Export as ${exportMetadata.extension.toUpperCase()}`}
1216
+ </button>
1217
+ ${
1218
+ isMarkdownTodo
1219
+ ? `
1220
+ <button
1221
+ class="standard-button"
1222
+ name="intent"
1223
+ type="submit"
1224
+ value="document"
1225
+ ${disabledAttribute}
1226
+ >
1227
+ ${modal.submitting ? "Working..." : "Export to document folder"}
1228
+ </button>
1229
+ `
1230
+ : ""
1231
+ }
1232
+ <button
1233
+ class="signature-button"
1234
+ name="intent"
1235
+ type="submit"
1236
+ value="copy"
1237
+ ${disabledAttribute}
1238
+ >
1239
+ ${modal.submitting ? "Working..." : "Copy"}
1240
+ </button>
1241
+ </div>
1059
1242
  </div>
1060
1243
  </form>
1061
1244
  `;
@@ -1230,7 +1413,7 @@ function renderTableDesignerConstraintsModal(modal, state) {
1230
1413
  <div class="table-designer-constraints-modal__empty">
1231
1414
  No active table designer draft.
1232
1415
  </div>
1233
- <div class="flex items-center justify-end gap-3 pt-2">
1416
+ <div class="flex items-center justify-between gap-3 pt-2">
1234
1417
  <button class="standard-button" data-action="close-modal" type="button">Close</button>
1235
1418
  </div>
1236
1419
  `;
@@ -1291,7 +1474,7 @@ function renderTableDesignerConstraintsModal(modal, state) {
1291
1474
  : "No multi-column or partial unique constraints detected.",
1292
1475
  body: uniqueConstraints.map(renderTableDesignerUniqueConstraintEditor).join(""),
1293
1476
  })}
1294
- <div class="flex items-center justify-end gap-3 pt-2">
1477
+ <div class="flex items-center justify-between gap-3 pt-2">
1295
1478
  <button class="standard-button" data-action="close-modal" type="button">Close</button>
1296
1479
  </div>
1297
1480
  </div>
@@ -1336,7 +1519,7 @@ function renderCreateMediaTaggingMappingTableForm(modal, state) {
1336
1519
  ${renderSqlPreviewField(MEDIA_TAGGING_DEFAULT_MAPPING_TABLE_SQL)}
1337
1520
  </div>
1338
1521
  ${renderError(modal.error)}
1339
- <div class="flex items-center justify-end gap-3 pt-2">
1522
+ <div class="flex items-center justify-between gap-3 pt-2">
1340
1523
  <button
1341
1524
  class="standard-button"
1342
1525
  data-action="close-modal"
@@ -1400,7 +1583,7 @@ function renderCreateMediaTaggingTagTableForm(modal, state) {
1400
1583
  ${renderSqlPreviewField(MEDIA_TAGGING_DEFAULT_TAG_TABLE_SQL)}
1401
1584
  </div>
1402
1585
  ${renderError(modal.error)}
1403
- <div class="flex items-center justify-end gap-3 pt-2">
1586
+ <div class="flex items-center justify-between gap-3 pt-2">
1404
1587
  <button
1405
1588
  class="standard-button"
1406
1589
  data-action="close-modal"
@@ -1479,6 +1662,21 @@ export function renderModal(state) {
1479
1662
  title: "Delete Query",
1480
1663
  body: renderDeleteQueryHistoryForm(modal),
1481
1664
  },
1665
+ "delete-document": {
1666
+ eyebrow: "Documents // Confirm deletion",
1667
+ title: "Delete Document",
1668
+ body: renderDeleteDocumentForm(modal),
1669
+ },
1670
+ "document-insert-table": {
1671
+ eyebrow: "Documents // Saved query output",
1672
+ title: "Insert Table",
1673
+ body: renderDocumentInsertTableForm(modal),
1674
+ },
1675
+ "document-insert-note": {
1676
+ eyebrow: "Documents // Saved query notes",
1677
+ title: "Insert Note",
1678
+ body: renderDocumentInsertNoteForm(modal),
1679
+ },
1482
1680
  "query-export": {
1483
1681
  eyebrow: "SQL Editor // Export query result",
1484
1682
  title: "Export Query",
@@ -1521,6 +1719,8 @@ export function renderModal(state) {
1521
1719
  <div class="fixed inset-0 z-50 flex items-center justify-center bg-background/85 px-4 backdrop-blur-sm">
1522
1720
  <div class="w-full ${
1523
1721
  modal.kind === "chart-editor" ||
1722
+ modal.kind === "document-insert-table" ||
1723
+ modal.kind === "document-insert-note" ||
1524
1724
  modal.kind === "row-update-preview" ||
1525
1725
  modal.kind === "table-designer-constraints"
1526
1726
  ? "max-w-3xl"
@@ -1,33 +1,31 @@
1
- import { escapeHtml } from "../utils/format.js";
1
+ import { escapeHtml } from '../utils/format.js';
2
2
 
3
- export function renderPageHeader({ eyebrow = "", title, subtitle = "", actions = "" }) {
4
- return `
3
+ export function renderPageHeader({ eyebrow = '', title, subtitle = '', actions = '' }) {
4
+ return `
5
5
  <div class="mb-10 flex flex-wrap items-end justify-between gap-6">
6
6
  <div>
7
7
  ${
8
- eyebrow
9
- ? `
8
+ eyebrow
9
+ ? `
10
10
  <div class="page-eyebrow">
11
11
  <span class="text-[#FCE300] text-xs font-mono font-bold tracking-widest uppercase">${escapeHtml(
12
- eyebrow
12
+ eyebrow,
13
13
  )}</span>
14
14
  <div class="page-eyebrow-line"></div>
15
15
  </div>
16
16
  `
17
- : ""
17
+ : ''
18
18
  }
19
- <h1 class="text-5xl font-headline font-bold text-[#FCE300] tracking-tighter uppercase">${escapeHtml(
20
- title
21
- )}</h1>
19
+ <h1 class="text-5xl font-headline font-bold text-[#FCE300] tracking-tighter uppercase">${escapeHtml(title)}</h1>
22
20
  ${
23
- subtitle
24
- ? `<p class="text-xs font-mono text-on-surface/40 mt-1 uppercase tracking-widest">${escapeHtml(
25
- subtitle
26
- )}</p>`
27
- : ""
21
+ subtitle
22
+ ? `<p class="text-xs font-mono text-on-surface/40 mt-1 uppercase tracking-widest">${escapeHtml(
23
+ subtitle,
24
+ )}</p>`
25
+ : ''
28
26
  }
29
27
  </div>
30
- ${actions ? `<div class="flex gap-3">${actions}</div>` : ""}
28
+ ${actions ? `<div class="flex gap-3">${actions}</div>` : ''}
31
29
  </div>
32
30
  `;
33
31
  }