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
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import { escapeHtml, highlightSql, truncateMiddle } from "../utils/format.js";
|
|
1
|
+
import { escapeHtml, formatNumber, highlightSql, truncateMiddle } from "../utils/format.js";
|
|
2
|
+
import {
|
|
3
|
+
buildCopyColumnText,
|
|
4
|
+
buildCopyColumnPreviewText,
|
|
5
|
+
getCopyColumnActionLabel,
|
|
6
|
+
getCopyColumnExportMetadata,
|
|
7
|
+
isMarkdownTodoCopyColumnMode,
|
|
8
|
+
} from "../utils/copyColumnExport.js";
|
|
2
9
|
import { renderConnectionLogo } from "./connectionLogo.js";
|
|
3
10
|
import {
|
|
4
11
|
analyzeQueryChartResult,
|
|
@@ -164,7 +171,7 @@ function renderOpenConnectionForm(modal) {
|
|
|
164
171
|
text: "Open read-only",
|
|
165
172
|
})}
|
|
166
173
|
${renderError(modal.error)}
|
|
167
|
-
<div class="flex items-center justify-
|
|
174
|
+
<div class="flex items-center justify-between gap-3 pt-2">
|
|
168
175
|
<button
|
|
169
176
|
class="standard-button"
|
|
170
177
|
data-action="close-modal"
|
|
@@ -238,7 +245,7 @@ function renderEditConnectionForm(modal) {
|
|
|
238
245
|
text: "Open read-only",
|
|
239
246
|
})}
|
|
240
247
|
${renderError(modal.error)}
|
|
241
|
-
<div class="flex items-center justify-
|
|
248
|
+
<div class="flex items-center justify-between gap-3 pt-2">
|
|
242
249
|
<button
|
|
243
250
|
class="standard-button"
|
|
244
251
|
data-action="close-modal"
|
|
@@ -271,7 +278,7 @@ function renderCreateDatabaseForm(modal) {
|
|
|
271
278
|
placeholder: "Optional display name",
|
|
272
279
|
})}
|
|
273
280
|
${renderError(modal.error)}
|
|
274
|
-
<div class="flex items-center justify-
|
|
281
|
+
<div class="flex items-center justify-between gap-3 pt-2">
|
|
275
282
|
<button
|
|
276
283
|
class="standard-button"
|
|
277
284
|
data-action="close-modal"
|
|
@@ -357,7 +364,7 @@ function renderImportSqlForm(modal, state) {
|
|
|
357
364
|
explicit path instead of file upload.
|
|
358
365
|
</p>
|
|
359
366
|
${renderError(modal.error)}
|
|
360
|
-
<div class="flex items-center justify-
|
|
367
|
+
<div class="flex items-center justify-between gap-3 pt-2">
|
|
361
368
|
<button
|
|
362
369
|
class="standard-button"
|
|
363
370
|
data-action="close-modal"
|
|
@@ -417,7 +424,7 @@ function renderDeleteRowConfirmForm(modal) {
|
|
|
417
424
|
rowPreviewMarkup,
|
|
418
425
|
"</div>",
|
|
419
426
|
renderError(modal.error),
|
|
420
|
-
'<div class="flex items-center justify-
|
|
427
|
+
'<div class="flex items-center justify-between gap-3 pt-2">',
|
|
421
428
|
'<button class="standard-button" data-action="close-modal" type="button">Cancel</button>',
|
|
422
429
|
'<button class="delete-button" type="submit">',
|
|
423
430
|
modal.submitting ? "Deleting..." : "Delete Row",
|
|
@@ -507,7 +514,7 @@ function renderRowUpdatePreviewForm(modal) {
|
|
|
507
514
|
</div>
|
|
508
515
|
${paramsMarkup}
|
|
509
516
|
${renderError(modal.error)}
|
|
510
|
-
<div class="flex items-center justify-
|
|
517
|
+
<div class="flex items-center justify-between gap-3 pt-2">
|
|
511
518
|
<button class="standard-button" data-action="close-modal" type="button">Cancel</button>
|
|
512
519
|
<button class="signature-button" type="submit">
|
|
513
520
|
${modal.submitting ? 'Applying...' : 'Apply Changes'}
|
|
@@ -747,7 +754,7 @@ function renderChartEditorForm(modal, state) {
|
|
|
747
754
|
: ""
|
|
748
755
|
}
|
|
749
756
|
${renderError(modal.error)}
|
|
750
|
-
<div class="flex items-center justify-
|
|
757
|
+
<div class="flex items-center justify-between gap-3 pt-2">
|
|
751
758
|
<button
|
|
752
759
|
class="standard-button"
|
|
753
760
|
data-action="close-modal"
|
|
@@ -775,7 +782,7 @@ function renderDeleteChartForm(modal) {
|
|
|
775
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>',
|
|
776
783
|
"</div>",
|
|
777
784
|
renderError(modal.error),
|
|
778
|
-
'<div class="flex items-center justify-
|
|
785
|
+
'<div class="flex items-center justify-between gap-3 pt-2">',
|
|
779
786
|
'<button class="standard-button" data-action="close-modal" type="button">Cancel</button>',
|
|
780
787
|
'<button class="delete-button" type="submit">',
|
|
781
788
|
modal.submitting ? "Deleting..." : "Delete Chart",
|
|
@@ -792,7 +799,7 @@ function renderDeleteQueryHistoryForm(modal) {
|
|
|
792
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>',
|
|
793
800
|
"</div>",
|
|
794
801
|
renderError(modal.error),
|
|
795
|
-
'<div class="flex items-center justify-
|
|
802
|
+
'<div class="flex items-center justify-between gap-3 pt-2">',
|
|
796
803
|
'<button class="standard-button" data-action="close-modal" type="button">Cancel</button>',
|
|
797
804
|
'<button class="delete-button" type="submit">',
|
|
798
805
|
modal.submitting ? "Deleting..." : "Delete Query",
|
|
@@ -800,6 +807,147 @@ function renderDeleteQueryHistoryForm(modal) {
|
|
|
800
807
|
].join("");
|
|
801
808
|
}
|
|
802
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
|
+
|
|
803
951
|
function renderQueryExportPreview(lines = []) {
|
|
804
952
|
return lines.map((line) => `<span class="block whitespace-pre">${escapeHtml(line)}</span>`).join("");
|
|
805
953
|
}
|
|
@@ -829,7 +977,21 @@ function renderTextExportModal(modal, action) {
|
|
|
829
977
|
const disabledAttribute = modal.submitting ? 'disabled aria-disabled="true"' : "";
|
|
830
978
|
|
|
831
979
|
return `
|
|
832
|
-
<div class="space-y-5">
|
|
980
|
+
<div class="space-y-5" data-export-modal>
|
|
981
|
+
<label class="block space-y-2">
|
|
982
|
+
<span class="text-[10px] font-mono uppercase tracking-[0.22em] text-on-surface-variant/60">
|
|
983
|
+
Filename
|
|
984
|
+
</span>
|
|
985
|
+
<input
|
|
986
|
+
autocomplete="off"
|
|
987
|
+
class="control-input w-full border border-outline-variant/20 bg-surface-container-lowest text-sm text-on-surface outline-none transition-colors focus:border-primary-container"
|
|
988
|
+
name="filename"
|
|
989
|
+
spellcheck="false"
|
|
990
|
+
type="text"
|
|
991
|
+
value="${escapeHtml(modal.filename ?? "")}"
|
|
992
|
+
${disabledAttribute}
|
|
993
|
+
/>
|
|
994
|
+
</label>
|
|
833
995
|
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
834
996
|
${getExportOptions()
|
|
835
997
|
.map(
|
|
@@ -868,7 +1030,7 @@ function renderTextExportModal(modal, action) {
|
|
|
868
1030
|
.join("")}
|
|
869
1031
|
</div>
|
|
870
1032
|
${renderError(modal.error)}
|
|
871
|
-
<div class="flex justify-
|
|
1033
|
+
<div class="flex justify-start">
|
|
872
1034
|
<button class="standard-button" data-action="close-modal" type="button">
|
|
873
1035
|
Cancel
|
|
874
1036
|
</button>
|
|
@@ -885,6 +1047,440 @@ function renderDataExportModal(modal) {
|
|
|
885
1047
|
return renderTextExportModal(modal, "export-data-format");
|
|
886
1048
|
}
|
|
887
1049
|
|
|
1050
|
+
function getCopyColumnResult(state, modal) {
|
|
1051
|
+
return modal.scope === "charts" ? state.charts.result : state.editor.result;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
function renderCopyColumnPreview(modal, state) {
|
|
1055
|
+
const result = getCopyColumnResult(state, modal);
|
|
1056
|
+
const isMarkdownTodo = isMarkdownTodoCopyColumnMode(modal.copyMode);
|
|
1057
|
+
const separator = Boolean(modal.lineBreaks) && !isMarkdownTodoCopyColumnMode(modal.copyMode)
|
|
1058
|
+
? "\n"
|
|
1059
|
+
: String(modal.separator ?? ",");
|
|
1060
|
+
const wrapper = String(modal.wrapper ?? '"');
|
|
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) {
|
|
1079
|
+
return "";
|
|
1080
|
+
}
|
|
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
|
+
|
|
1097
|
+
return `
|
|
1098
|
+
<div class="space-y-2">
|
|
1099
|
+
<div class="text-[10px] font-mono uppercase tracking-[0.22em] text-on-surface-variant/60">
|
|
1100
|
+
Preview
|
|
1101
|
+
</div>
|
|
1102
|
+
<pre class="copy-column-preview custom-scrollbar">${escapeHtml(preview)}</pre>
|
|
1103
|
+
</div>
|
|
1104
|
+
`;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
function renderCopyColumnLineBreaksField({ checked = false, disabled = false } = {}) {
|
|
1108
|
+
return `
|
|
1109
|
+
<label class="block space-y-2">
|
|
1110
|
+
<span class="text-[10px] font-mono uppercase tracking-[0.22em] text-on-surface-variant/60">
|
|
1111
|
+
Format
|
|
1112
|
+
</span>
|
|
1113
|
+
<span class="standard-checkbox ${disabled ? "is-disabled" : ""}">
|
|
1114
|
+
<input
|
|
1115
|
+
${checked ? "checked" : ""}
|
|
1116
|
+
${disabled ? "disabled" : ""}
|
|
1117
|
+
data-bind="copy-column-format-field"
|
|
1118
|
+
data-field="lineBreaks"
|
|
1119
|
+
name="lineBreaks"
|
|
1120
|
+
type="checkbox"
|
|
1121
|
+
/>
|
|
1122
|
+
<span>Line breaks</span>
|
|
1123
|
+
</span>
|
|
1124
|
+
</label>
|
|
1125
|
+
`;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
function renderCopyColumnFormatField({ label, name, value = "" }) {
|
|
1129
|
+
return `
|
|
1130
|
+
<label class="block space-y-2">
|
|
1131
|
+
<span class="text-[10px] font-mono uppercase tracking-[0.22em] text-on-surface-variant/60">
|
|
1132
|
+
${escapeHtml(label)}
|
|
1133
|
+
</span>
|
|
1134
|
+
<input
|
|
1135
|
+
class="control-input w-full border border-outline-variant/20 bg-surface-container-lowest text-sm text-on-surface outline-none transition-colors focus:border-primary-container"
|
|
1136
|
+
data-bind="copy-column-format-field"
|
|
1137
|
+
data-field="${escapeHtml(name)}"
|
|
1138
|
+
name="${escapeHtml(name)}"
|
|
1139
|
+
type="text"
|
|
1140
|
+
value="${escapeHtml(value)}"
|
|
1141
|
+
/>
|
|
1142
|
+
</label>
|
|
1143
|
+
`;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
export function renderCopyColumnModal(modal, state) {
|
|
1147
|
+
const result = getCopyColumnResult(state, modal);
|
|
1148
|
+
const rows = result?.rows ?? [];
|
|
1149
|
+
const valueCount = modal.copyMode === "first-10" ? Math.min(rows.length, 10) : rows.length;
|
|
1150
|
+
const disabledAttribute = modal.submitting ? 'disabled aria-disabled="true"' : "";
|
|
1151
|
+
const exportMetadata = getCopyColumnExportMetadata(modal.copyMode);
|
|
1152
|
+
const isMarkdownTodo = isMarkdownTodoCopyColumnMode(modal.copyMode);
|
|
1153
|
+
const lineBreaks = isMarkdownTodo || Boolean(modal.lineBreaks);
|
|
1154
|
+
const formatFieldsMarkup = isMarkdownTodo
|
|
1155
|
+
? renderCopyColumnLineBreaksField({
|
|
1156
|
+
checked: true,
|
|
1157
|
+
disabled: true,
|
|
1158
|
+
})
|
|
1159
|
+
: `
|
|
1160
|
+
<div class="grid grid-cols-1 gap-4 sm:grid-cols-3">
|
|
1161
|
+
${renderCopyColumnFormatField({
|
|
1162
|
+
label: "Separator",
|
|
1163
|
+
name: "separator",
|
|
1164
|
+
value: modal.separator ?? ",",
|
|
1165
|
+
})}
|
|
1166
|
+
${renderCopyColumnFormatField({
|
|
1167
|
+
label: "Wrapper",
|
|
1168
|
+
name: "wrapper",
|
|
1169
|
+
value: modal.wrapper ?? '"',
|
|
1170
|
+
})}
|
|
1171
|
+
${renderCopyColumnLineBreaksField({
|
|
1172
|
+
checked: lineBreaks,
|
|
1173
|
+
})}
|
|
1174
|
+
</div>
|
|
1175
|
+
`;
|
|
1176
|
+
|
|
1177
|
+
return `
|
|
1178
|
+
<form class="space-y-5" data-form="copy-column">
|
|
1179
|
+
<input name="scope" type="hidden" value="${escapeHtml(modal.scope ?? "editor")}" />
|
|
1180
|
+
<input name="columnName" type="hidden" value="${escapeHtml(modal.columnName ?? "")}" />
|
|
1181
|
+
<input name="copyMode" type="hidden" value="${escapeHtml(modal.copyMode ?? "column")}" />
|
|
1182
|
+
<div class="border border-outline-variant/10 bg-surface-container-lowest px-4 py-3">
|
|
1183
|
+
<div class="text-[10px] font-mono uppercase tracking-[0.22em] text-on-surface-variant/60">
|
|
1184
|
+
Column
|
|
1185
|
+
</div>
|
|
1186
|
+
<div class="mt-2 flex min-w-0 items-center justify-between gap-4">
|
|
1187
|
+
<code class="min-w-0 truncate font-mono text-sm text-primary-container" title="${escapeHtml(
|
|
1188
|
+
modal.columnName ?? ""
|
|
1189
|
+
)}">${escapeHtml(modal.columnName ?? "")}</code>
|
|
1190
|
+
<span class="shrink-0 font-mono text-[10px] uppercase tracking-[0.18em] text-on-surface-variant/50">
|
|
1191
|
+
${escapeHtml(getCopyColumnActionLabel(modal.copyMode))} · ${formatNumber(valueCount)}
|
|
1192
|
+
</span>
|
|
1193
|
+
</div>
|
|
1194
|
+
</div>
|
|
1195
|
+
${formatFieldsMarkup}
|
|
1196
|
+
${renderCopyColumnPreview(modal, state)}
|
|
1197
|
+
${renderError(modal.error)}
|
|
1198
|
+
<div class="flex flex-wrap items-center justify-between gap-3 pt-2">
|
|
1199
|
+
<button
|
|
1200
|
+
class="standard-button"
|
|
1201
|
+
data-action="close-modal"
|
|
1202
|
+
type="button"
|
|
1203
|
+
${disabledAttribute}
|
|
1204
|
+
>
|
|
1205
|
+
Cancel
|
|
1206
|
+
</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>
|
|
1242
|
+
</div>
|
|
1243
|
+
</form>
|
|
1244
|
+
`;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
function getTableDesignerUniqueConstraintTypeLabel(constraint) {
|
|
1248
|
+
if (constraint.partial) {
|
|
1249
|
+
return "Partial unique index";
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
if ((constraint.columns?.length ?? 0) > 1) {
|
|
1253
|
+
return "Multi-column unique";
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
return "Unique constraint";
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
function renderTableDesignerUniqueConstraintExpression(constraint) {
|
|
1260
|
+
const expression = String(constraint.expression || constraint.sql || "").trim();
|
|
1261
|
+
|
|
1262
|
+
if (expression) {
|
|
1263
|
+
return expression;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
const columns = (constraint.columns ?? []).map((column) => column.name).filter(Boolean);
|
|
1267
|
+
return columns.length ? `UNIQUE (${columns.join(", ")})` : "UNIQUE constraint";
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
function renderTableDesignerCheckConstraintExpression(constraint) {
|
|
1271
|
+
return String(constraint.expression || "").trim() || "CHECK constraint";
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
function normalizeTableDesignerConstraintColumnName(name) {
|
|
1275
|
+
return String(name ?? "").trim().toLowerCase();
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
function tableDesignerConstraintIncludesColumn(constraint, columnName) {
|
|
1279
|
+
const normalizedColumn = normalizeTableDesignerConstraintColumnName(columnName);
|
|
1280
|
+
|
|
1281
|
+
if (!normalizedColumn) {
|
|
1282
|
+
return false;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
return (constraint.columns ?? []).some(
|
|
1286
|
+
(constraintColumn) =>
|
|
1287
|
+
normalizeTableDesignerConstraintColumnName(constraintColumn.name) === normalizedColumn
|
|
1288
|
+
);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
function renderTableDesignerUniqueConstraintEditor(constraint) {
|
|
1292
|
+
const columns = (constraint.columns ?? []).map((column) => column.name).filter(Boolean);
|
|
1293
|
+
|
|
1294
|
+
return `
|
|
1295
|
+
<article class="table-designer-constraint-editor">
|
|
1296
|
+
<div class="table-designer-constraint-editor__header">
|
|
1297
|
+
<div class="min-w-0">
|
|
1298
|
+
<div class="table-designer-constraint-editor__type">
|
|
1299
|
+
${escapeHtml(getTableDesignerUniqueConstraintTypeLabel(constraint))}
|
|
1300
|
+
${constraint.origin ? ` // origin ${escapeHtml(constraint.origin)}` : ""}
|
|
1301
|
+
</div>
|
|
1302
|
+
<input
|
|
1303
|
+
class="table-designer-field table-designer-constraint-editor__name"
|
|
1304
|
+
data-bind="table-designer-constraint-field"
|
|
1305
|
+
data-constraint-id="${escapeHtml(constraint.id)}"
|
|
1306
|
+
data-constraint-kind="unique"
|
|
1307
|
+
data-field="name"
|
|
1308
|
+
spellcheck="false"
|
|
1309
|
+
type="text"
|
|
1310
|
+
value="${escapeHtml(constraint.name || "UNIQUE constraint")}"
|
|
1311
|
+
/>
|
|
1312
|
+
</div>
|
|
1313
|
+
<div class="status-badge status-badge--muted">Rebuild</div>
|
|
1314
|
+
</div>
|
|
1315
|
+
${
|
|
1316
|
+
columns.length
|
|
1317
|
+
? `
|
|
1318
|
+
<div class="table-designer-constraint__columns">
|
|
1319
|
+
${columns.map((column) => `<span>${escapeHtml(column)}</span>`).join("")}
|
|
1320
|
+
</div>
|
|
1321
|
+
`
|
|
1322
|
+
: ""
|
|
1323
|
+
}
|
|
1324
|
+
<textarea
|
|
1325
|
+
class="table-designer-constraint-editor__sql custom-scrollbar"
|
|
1326
|
+
data-bind="table-designer-constraint-field"
|
|
1327
|
+
data-constraint-id="${escapeHtml(constraint.id)}"
|
|
1328
|
+
data-constraint-kind="unique"
|
|
1329
|
+
data-field="expression"
|
|
1330
|
+
spellcheck="false"
|
|
1331
|
+
>${escapeHtml(renderTableDesignerUniqueConstraintExpression(constraint))}</textarea>
|
|
1332
|
+
</article>
|
|
1333
|
+
`;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
function renderTableDesignerCheckConstraintEditor(constraint) {
|
|
1337
|
+
const columns = constraint.columns ?? [];
|
|
1338
|
+
const allowedValues = columns.flatMap((column) =>
|
|
1339
|
+
(column.allowedValues ?? []).map((value) => ({
|
|
1340
|
+
columnName: column.name,
|
|
1341
|
+
value,
|
|
1342
|
+
}))
|
|
1343
|
+
);
|
|
1344
|
+
|
|
1345
|
+
return `
|
|
1346
|
+
<article class="table-designer-constraint-editor">
|
|
1347
|
+
<div class="table-designer-constraint-editor__header">
|
|
1348
|
+
<div class="min-w-0">
|
|
1349
|
+
<div class="table-designer-constraint-editor__type">
|
|
1350
|
+
${columns.length ? escapeHtml(columns.map((column) => column.name).join(", ")) : "Table check"}
|
|
1351
|
+
</div>
|
|
1352
|
+
<input
|
|
1353
|
+
class="table-designer-field table-designer-constraint-editor__name"
|
|
1354
|
+
data-bind="table-designer-constraint-field"
|
|
1355
|
+
data-constraint-id="${escapeHtml(constraint.id)}"
|
|
1356
|
+
data-constraint-kind="check"
|
|
1357
|
+
data-field="name"
|
|
1358
|
+
spellcheck="false"
|
|
1359
|
+
type="text"
|
|
1360
|
+
value="${escapeHtml(constraint.name || "CHECK constraint")}"
|
|
1361
|
+
/>
|
|
1362
|
+
</div>
|
|
1363
|
+
<div class="status-badge status-badge--muted">Rebuild</div>
|
|
1364
|
+
</div>
|
|
1365
|
+
${
|
|
1366
|
+
allowedValues.length
|
|
1367
|
+
? `
|
|
1368
|
+
<div class="table-designer-constraint__values custom-scrollbar">
|
|
1369
|
+
${allowedValues
|
|
1370
|
+
.map(
|
|
1371
|
+
(entry) => `
|
|
1372
|
+
<span title="${escapeHtml(entry.columnName)}">${escapeHtml(entry.value)}</span>
|
|
1373
|
+
`
|
|
1374
|
+
)
|
|
1375
|
+
.join("")}
|
|
1376
|
+
</div>
|
|
1377
|
+
`
|
|
1378
|
+
: ""
|
|
1379
|
+
}
|
|
1380
|
+
<textarea
|
|
1381
|
+
class="table-designer-constraint-editor__sql custom-scrollbar"
|
|
1382
|
+
data-bind="table-designer-constraint-field"
|
|
1383
|
+
data-constraint-id="${escapeHtml(constraint.id)}"
|
|
1384
|
+
data-constraint-kind="check"
|
|
1385
|
+
data-field="expression"
|
|
1386
|
+
spellcheck="false"
|
|
1387
|
+
>${escapeHtml(renderTableDesignerCheckConstraintExpression(constraint))}</textarea>
|
|
1388
|
+
</article>
|
|
1389
|
+
`;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
function renderTableDesignerConstraintSection({ title, count, emptyText, body }) {
|
|
1393
|
+
return `
|
|
1394
|
+
<section class="table-designer-constraints-modal__section">
|
|
1395
|
+
<div class="table-designer-constraints-modal__section-header">
|
|
1396
|
+
<div class="table-designer-constraints-modal__section-title">${escapeHtml(title)}</div>
|
|
1397
|
+
<div class="status-badge status-badge--muted">${formatNumber(count)}</div>
|
|
1398
|
+
</div>
|
|
1399
|
+
${
|
|
1400
|
+
count
|
|
1401
|
+
? `<div class="table-designer-constraints__list">${body}</div>`
|
|
1402
|
+
: `<div class="table-designer-constraints-modal__empty">${escapeHtml(emptyText)}</div>`
|
|
1403
|
+
}
|
|
1404
|
+
</section>
|
|
1405
|
+
`;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
function renderTableDesignerConstraintsModal(modal, state) {
|
|
1409
|
+
const draft = state.tableDesigner?.draft;
|
|
1410
|
+
|
|
1411
|
+
if (!draft) {
|
|
1412
|
+
return `
|
|
1413
|
+
<div class="table-designer-constraints-modal__empty">
|
|
1414
|
+
No active table designer draft.
|
|
1415
|
+
</div>
|
|
1416
|
+
<div class="flex items-center justify-between gap-3 pt-2">
|
|
1417
|
+
<button class="standard-button" data-action="close-modal" type="button">Close</button>
|
|
1418
|
+
</div>
|
|
1419
|
+
`;
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
const selectedColumn = modal.columnId
|
|
1423
|
+
? (draft.columns ?? []).find((column) => column.id === modal.columnId)
|
|
1424
|
+
: null;
|
|
1425
|
+
const selectedColumnName = String(modal.columnName ?? selectedColumn?.name ?? "").trim();
|
|
1426
|
+
const hasSelectedColumn = Boolean(modal.columnId || selectedColumnName);
|
|
1427
|
+
const selectedColumnLabel = selectedColumnName || "Unnamed column";
|
|
1428
|
+
const allUniqueConstraints = draft.mode === "edit" ? draft.uniqueConstraints ?? [] : [];
|
|
1429
|
+
const allCheckConstraints = draft.mode === "edit" ? draft.checkConstraints ?? [] : [];
|
|
1430
|
+
const uniqueConstraints = hasSelectedColumn
|
|
1431
|
+
? allUniqueConstraints.filter((constraint) =>
|
|
1432
|
+
tableDesignerConstraintIncludesColumn(constraint, selectedColumnName)
|
|
1433
|
+
)
|
|
1434
|
+
: allUniqueConstraints;
|
|
1435
|
+
const checkConstraints = hasSelectedColumn
|
|
1436
|
+
? allCheckConstraints.filter((constraint) =>
|
|
1437
|
+
tableDesignerConstraintIncludesColumn(constraint, selectedColumnName)
|
|
1438
|
+
)
|
|
1439
|
+
: allCheckConstraints;
|
|
1440
|
+
const totalCount = uniqueConstraints.length + checkConstraints.length;
|
|
1441
|
+
|
|
1442
|
+
return `
|
|
1443
|
+
<div class="table-designer-constraints-modal">
|
|
1444
|
+
<div class="table-designer-constraints-modal__summary">
|
|
1445
|
+
<div class="min-w-0">
|
|
1446
|
+
<div class="table-designer-constraints-modal__label">Table</div>
|
|
1447
|
+
<code title="${escapeHtml(draft.tableName ?? "")}">${escapeHtml(draft.tableName ?? "")}</code>
|
|
1448
|
+
</div>
|
|
1449
|
+
${
|
|
1450
|
+
hasSelectedColumn
|
|
1451
|
+
? `
|
|
1452
|
+
<div class="min-w-0">
|
|
1453
|
+
<div class="table-designer-constraints-modal__label">Column</div>
|
|
1454
|
+
<code title="${escapeHtml(selectedColumnLabel)}">${escapeHtml(selectedColumnLabel)}</code>
|
|
1455
|
+
</div>
|
|
1456
|
+
`
|
|
1457
|
+
: ""
|
|
1458
|
+
}
|
|
1459
|
+
<div class="status-badge status-badge--primary">V2 · ${formatNumber(totalCount)}</div>
|
|
1460
|
+
</div>
|
|
1461
|
+
${renderTableDesignerConstraintSection({
|
|
1462
|
+
title: "Check constraints",
|
|
1463
|
+
count: checkConstraints.length,
|
|
1464
|
+
emptyText: hasSelectedColumn
|
|
1465
|
+
? "No check constraints detected for this column."
|
|
1466
|
+
: "No check constraints detected.",
|
|
1467
|
+
body: checkConstraints.map(renderTableDesignerCheckConstraintEditor).join(""),
|
|
1468
|
+
})}
|
|
1469
|
+
${renderTableDesignerConstraintSection({
|
|
1470
|
+
title: hasSelectedColumn ? "Related unique constraints" : "Unique constraints",
|
|
1471
|
+
count: uniqueConstraints.length,
|
|
1472
|
+
emptyText: hasSelectedColumn
|
|
1473
|
+
? "No related multi-column or partial unique constraints detected."
|
|
1474
|
+
: "No multi-column or partial unique constraints detected.",
|
|
1475
|
+
body: uniqueConstraints.map(renderTableDesignerUniqueConstraintEditor).join(""),
|
|
1476
|
+
})}
|
|
1477
|
+
<div class="flex items-center justify-between gap-3 pt-2">
|
|
1478
|
+
<button class="standard-button" data-action="close-modal" type="button">Close</button>
|
|
1479
|
+
</div>
|
|
1480
|
+
</div>
|
|
1481
|
+
`;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
888
1484
|
function renderCreateMediaTaggingMappingTableForm(modal, state) {
|
|
889
1485
|
const mappingExists = hasDefaultMediaTaggingMappingTable(state.mediaTagging.schemaTables ?? []);
|
|
890
1486
|
const readOnly = Boolean(state.mediaTagging.connection?.readOnly);
|
|
@@ -923,7 +1519,7 @@ function renderCreateMediaTaggingMappingTableForm(modal, state) {
|
|
|
923
1519
|
${renderSqlPreviewField(MEDIA_TAGGING_DEFAULT_MAPPING_TABLE_SQL)}
|
|
924
1520
|
</div>
|
|
925
1521
|
${renderError(modal.error)}
|
|
926
|
-
<div class="flex items-center justify-
|
|
1522
|
+
<div class="flex items-center justify-between gap-3 pt-2">
|
|
927
1523
|
<button
|
|
928
1524
|
class="standard-button"
|
|
929
1525
|
data-action="close-modal"
|
|
@@ -987,7 +1583,7 @@ function renderCreateMediaTaggingTagTableForm(modal, state) {
|
|
|
987
1583
|
${renderSqlPreviewField(MEDIA_TAGGING_DEFAULT_TAG_TABLE_SQL)}
|
|
988
1584
|
</div>
|
|
989
1585
|
${renderError(modal.error)}
|
|
990
|
-
<div class="flex items-center justify-
|
|
1586
|
+
<div class="flex items-center justify-between gap-3 pt-2">
|
|
991
1587
|
<button
|
|
992
1588
|
class="standard-button"
|
|
993
1589
|
data-action="close-modal"
|
|
@@ -1066,6 +1662,21 @@ export function renderModal(state) {
|
|
|
1066
1662
|
title: "Delete Query",
|
|
1067
1663
|
body: renderDeleteQueryHistoryForm(modal),
|
|
1068
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
|
+
},
|
|
1069
1680
|
"query-export": {
|
|
1070
1681
|
eyebrow: "SQL Editor // Export query result",
|
|
1071
1682
|
title: "Export Query",
|
|
@@ -1076,6 +1687,16 @@ export function renderModal(state) {
|
|
|
1076
1687
|
title: "Export Table",
|
|
1077
1688
|
body: renderDataExportModal(modal),
|
|
1078
1689
|
},
|
|
1690
|
+
"copy-column": {
|
|
1691
|
+
eyebrow: "Results // Copy or export column values",
|
|
1692
|
+
title: isMarkdownTodoCopyColumnMode(modal.copyMode) ? "Export Markdown Todo" : "Copy column",
|
|
1693
|
+
body: renderCopyColumnModal(modal, state),
|
|
1694
|
+
},
|
|
1695
|
+
"table-designer-constraints": {
|
|
1696
|
+
eyebrow: "Table Designer // Constraints",
|
|
1697
|
+
title: "Checks",
|
|
1698
|
+
body: renderTableDesignerConstraintsModal(modal, state),
|
|
1699
|
+
},
|
|
1079
1700
|
"create-media-tagging-tag-table": {
|
|
1080
1701
|
eyebrow: "Media Tagging // Create default tag table",
|
|
1081
1702
|
title: "Create Tag Table",
|
|
@@ -1096,7 +1717,15 @@ export function renderModal(state) {
|
|
|
1096
1717
|
|
|
1097
1718
|
return `
|
|
1098
1719
|
<div class="fixed inset-0 z-50 flex items-center justify-center bg-background/85 px-4 backdrop-blur-sm">
|
|
1099
|
-
<div class="w-full ${
|
|
1720
|
+
<div class="w-full ${
|
|
1721
|
+
modal.kind === "chart-editor" ||
|
|
1722
|
+
modal.kind === "document-insert-table" ||
|
|
1723
|
+
modal.kind === "document-insert-note" ||
|
|
1724
|
+
modal.kind === "row-update-preview" ||
|
|
1725
|
+
modal.kind === "table-designer-constraints"
|
|
1726
|
+
? "max-w-3xl"
|
|
1727
|
+
: "max-w-xl"
|
|
1728
|
+
} border border-outline-variant/20 bg-surface-container shadow-[0_24px_80px_rgba(0,0,0,0.45)]">
|
|
1100
1729
|
<div class="flex items-start justify-between gap-4 border-b border-outline-variant/10 bg-surface-container-low px-6 py-5">
|
|
1101
1730
|
<div>
|
|
1102
1731
|
<div class="text-[10px] font-mono uppercase tracking-[0.26em] text-primary-container/70">
|