From 5e8c754f412b941f0ececb377d98b851a1161bbd Mon Sep 17 00:00:00 2001
From: miracle
Date: Sun, 18 Oct 2026 12:27:34 +0800
Subject: [PATCH] feat(grid): configurable column metadata placement
---
.../editor/EditorSettingsDialog.vue | 30 +++++-
...gsDialog.externalClose.integration.spec.ts | 25 ++++-
apps/desktop/src/components/grid/DataGrid.vue | 46 +++++++--
.../__tests__/DataGridTransposeSort.spec.ts | 95 ++++++++++++++++++-
apps/desktop/src/i18n/locales/en.ts | 5 +
apps/desktop/src/i18n/locales/es.ts | 5 +
apps/desktop/src/i18n/locales/it.ts | 5 +
apps/desktop/src/i18n/locales/ja.ts | 5 +
apps/desktop/src/i18n/locales/ko.ts | 5 +
apps/desktop/src/i18n/locales/pt-BR.ts | 5 +
apps/desktop/src/i18n/locales/ru.ts | 5 +
apps/desktop/src/i18n/locales/th.ts | 5 +
apps/desktop/src/i18n/locales/zh-CN.ts | 5 +
apps/desktop/src/i18n/locales/zh-TW.ts | 5 +
.../__tests__/editorSettingsDraft.spec.ts | 6 ++
.../__tests__/settingsTransfer.spec.ts | 11 +++
.../src/lib/settings/editorSettingsDraft.ts | 2 +
.../src/lib/settings/settingsSearch.ts | 2 +
.../src/lib/settings/settingsTransfer.ts | 4 +
apps/desktop/src/stores/settingsStore.ts | 8 ++
packages/app-tests/settingsStore.test.ts | 22 +++++
21 files changed, 283 insertions(+), 18 deletions(-)
diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue
index e8efa86437..82952b0906 100644
--- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue
+++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue
@@ -815,12 +815,14 @@ const editSidebarTablePageSize = ref(settingsStore.desktopSettings.sidebar_table
const debugLogCopied = ref(false);
const debugLogDownloaded = ref(false);
const editShowColumnCommentsInHeader = ref(settingsStore.editorSettings.showColumnCommentsInHeader);
+const editDataGridColumnCommentsInInfoArea = ref(settingsStore.editorSettings.dataGridColumnCommentsInInfoArea);
const editShowColumnTypesInHeader = ref(settingsStore.editorSettings.showColumnTypesInHeader);
const editShowColumnHeaderTooltips = ref(settingsStore.editorSettings.showColumnHeaderTooltips);
const editShowResultSourceDatabase = ref(settingsStore.editorSettings.showResultSourceDatabase);
const editResultTabNamingMode = ref(settingsStore.editorSettings.resultTabNamingMode);
const editResultTabPreferComments = ref(settingsStore.editorSettings.resultTabPreferComments);
const editDataGridShowTransposeFieldMetadata = ref(settingsStore.editorSettings.dataGridShowTransposeFieldMetadata);
+const editDataGridTransposeMetadataInInfoColumn = ref(settingsStore.editorSettings.dataGridTransposeMetadataInInfoColumn);
const editColorizeDataGridCellTypes = ref(settingsStore.editorSettings.colorizeDataGridCellTypes);
const editShowIndexIndicatorsInHeader = ref(settingsStore.editorSettings.showIndexIndicatorsInHeader);
const editCompactColumnHeaderActions = ref(settingsStore.editorSettings.compactColumnHeaderActions);
@@ -1196,12 +1198,14 @@ function currentEditorSettingsDraft(): EditorSettingsDraft {
tabSortMode: editTabSortMode.value,
tabMaxWidth: editTabMaxWidth.value,
showColumnCommentsInHeader: editShowColumnCommentsInHeader.value,
+ dataGridColumnCommentsInInfoArea: editDataGridColumnCommentsInInfoArea.value,
showColumnTypesInHeader: editShowColumnTypesInHeader.value,
showColumnHeaderTooltips: editShowColumnHeaderTooltips.value,
showResultSourceDatabase: editShowResultSourceDatabase.value,
resultTabNamingMode: editResultTabNamingMode.value,
resultTabPreferComments: editResultTabPreferComments.value,
dataGridShowTransposeFieldMetadata: editDataGridShowTransposeFieldMetadata.value,
+ dataGridTransposeMetadataInInfoColumn: editDataGridTransposeMetadataInInfoColumn.value,
colorizeDataGridCellTypes: editColorizeDataGridCellTypes.value,
dataGridTypeColorSchemes: editDataGridTypeColorSchemes.value,
activeDataGridTypeColorSchemeId: editActiveDataGridTypeColorSchemeId.value,
@@ -1891,12 +1895,14 @@ function syncEditorSettingsDraftFromStore() {
editTabSortMode.value = settingsStore.editorSettings.tabSortMode;
editTabMaxWidth.value = settingsStore.editorSettings.tabMaxWidth;
editShowColumnCommentsInHeader.value = settingsStore.editorSettings.showColumnCommentsInHeader;
+ editDataGridColumnCommentsInInfoArea.value = settingsStore.editorSettings.dataGridColumnCommentsInInfoArea;
editShowColumnTypesInHeader.value = settingsStore.editorSettings.showColumnTypesInHeader;
editShowColumnHeaderTooltips.value = settingsStore.editorSettings.showColumnHeaderTooltips;
editShowResultSourceDatabase.value = settingsStore.editorSettings.showResultSourceDatabase;
editResultTabNamingMode.value = settingsStore.editorSettings.resultTabNamingMode;
editResultTabPreferComments.value = settingsStore.editorSettings.resultTabPreferComments;
editDataGridShowTransposeFieldMetadata.value = settingsStore.editorSettings.dataGridShowTransposeFieldMetadata;
+ editDataGridTransposeMetadataInInfoColumn.value = settingsStore.editorSettings.dataGridTransposeMetadataInInfoColumn;
editColorizeDataGridCellTypes.value = settingsStore.editorSettings.colorizeDataGridCellTypes;
editDataGridTypeColorSchemes.value = cloneDataGridTypeColorSchemes(settingsStore.editorSettings.dataGridTypeColorSchemes);
editActiveDataGridTypeColorSchemeId.value = settingsStore.editorSettings.activeDataGridTypeColorSchemeId;
@@ -2060,12 +2066,14 @@ const editorSettingsDraftRefs: EditorSettingsDraftRefMap = {
tabSortMode: editTabSortMode,
tabMaxWidth: editTabMaxWidth,
showColumnCommentsInHeader: editShowColumnCommentsInHeader,
+ dataGridColumnCommentsInInfoArea: editDataGridColumnCommentsInInfoArea,
showColumnTypesInHeader: editShowColumnTypesInHeader,
showColumnHeaderTooltips: editShowColumnHeaderTooltips,
showResultSourceDatabase: editShowResultSourceDatabase,
resultTabNamingMode: editResultTabNamingMode,
resultTabPreferComments: editResultTabPreferComments,
dataGridShowTransposeFieldMetadata: editDataGridShowTransposeFieldMetadata,
+ dataGridTransposeMetadataInInfoColumn: editDataGridTransposeMetadataInInfoColumn,
colorizeDataGridCellTypes: editColorizeDataGridCellTypes,
dataGridTypeColorSchemes: editDataGridTypeColorSchemes,
activeDataGridTypeColorSchemeId: editActiveDataGridTypeColorSchemeId,
@@ -2705,12 +2713,14 @@ function resetDefaultsForTab(tab: SettingsCategory) {
historyRetention.reset();
mcpHistoryRetention.reset();
editShowColumnCommentsInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnCommentsInHeader;
+ editDataGridColumnCommentsInInfoArea.value = DEFAULT_EDITOR_SETTINGS.dataGridColumnCommentsInInfoArea;
editShowColumnTypesInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnTypesInHeader;
editShowColumnHeaderTooltips.value = DEFAULT_EDITOR_SETTINGS.showColumnHeaderTooltips;
editShowResultSourceDatabase.value = DEFAULT_EDITOR_SETTINGS.showResultSourceDatabase;
editResultTabNamingMode.value = DEFAULT_EDITOR_SETTINGS.resultTabNamingMode;
editResultTabPreferComments.value = DEFAULT_EDITOR_SETTINGS.resultTabPreferComments;
editDataGridShowTransposeFieldMetadata.value = DEFAULT_EDITOR_SETTINGS.dataGridShowTransposeFieldMetadata;
+ editDataGridTransposeMetadataInInfoColumn.value = DEFAULT_EDITOR_SETTINGS.dataGridTransposeMetadataInInfoColumn;
editColorizeDataGridCellTypes.value = DEFAULT_EDITOR_SETTINGS.colorizeDataGridCellTypes;
// Back to the built-in palette, but keep the user's saved schemes available.
editActiveDataGridTypeColorSchemeId.value = DEFAULT_EDITOR_SETTINGS.activeDataGridTypeColorSchemeId;
@@ -2841,9 +2851,11 @@ function resetAllDefaults() {
editDuckDbWorkerMaxProcesses.value = DEFAULT_DESKTOP_SETTINGS.duckdb_worker_max_processes;
editSidebarTablePageSize.value = DEFAULT_SIDEBAR_TABLE_PAGE_SIZE;
editShowColumnCommentsInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnCommentsInHeader;
+ editDataGridColumnCommentsInInfoArea.value = DEFAULT_EDITOR_SETTINGS.dataGridColumnCommentsInInfoArea;
editShowColumnTypesInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnTypesInHeader;
editShowColumnHeaderTooltips.value = DEFAULT_EDITOR_SETTINGS.showColumnHeaderTooltips;
editDataGridShowTransposeFieldMetadata.value = DEFAULT_EDITOR_SETTINGS.dataGridShowTransposeFieldMetadata;
+ editDataGridTransposeMetadataInInfoColumn.value = DEFAULT_EDITOR_SETTINGS.dataGridTransposeMetadataInInfoColumn;
editColorizeDataGridCellTypes.value = DEFAULT_EDITOR_SETTINGS.colorizeDataGridCellTypes;
// Reset the selection only; saved schemes survive a full settings reset.
editActiveDataGridTypeColorSchemeId.value = DEFAULT_EDITOR_SETTINGS.activeDataGridTypeColorSchemeId;
@@ -9749,7 +9761,14 @@ onUnmounted(() => {
{{ t("settings.showColumnCommentsInHeaderDescription") }}
-
+
+
+
+
+
+
{{ t("settings.dataGridColumnCommentsInInfoAreaDescription") }}
+
+
@@ -9820,7 +9839,14 @@ onUnmounted(() => {
{{ t("settings.dataGridShowTransposeFieldMetadataDescription") }}
-
+
+
+
+
+
+
{{ t("settings.dataGridTransposeMetadataInInfoColumnDescription") }}
+
+
diff --git a/apps/desktop/src/components/editor/__tests__/EditorSettingsDialog.externalClose.integration.spec.ts b/apps/desktop/src/components/editor/__tests__/EditorSettingsDialog.externalClose.integration.spec.ts
index 74128f28ae..524f6df39c 100644
--- a/apps/desktop/src/components/editor/__tests__/EditorSettingsDialog.externalClose.integration.spec.ts
+++ b/apps/desktop/src/components/editor/__tests__/EditorSettingsDialog.externalClose.integration.spec.ts
@@ -54,7 +54,7 @@ afterEach(() => {
document.body.innerHTML = "";
});
-async function mountPage() {
+async function mountPage(initialTab: "editor" | "data" = "editor") {
const pinia = createPinia();
setActivePinia(pinia);
const store = useSettingsStore();
@@ -73,7 +73,7 @@ async function mountPage() {
ref: page,
variant: "page",
open: true,
- initialTab: "editor",
+ initialTab,
"onUpdate:open": (value: boolean) => {
open.value = value;
},
@@ -102,6 +102,27 @@ function clickText(key: string) {
}
describe("settings page external close request", () => {
+ it("saves and restores independent normal and transpose information placement", async () => {
+ const { page, open, store } = await mountPage("data");
+ const transposeSwitch = () => host.querySelector
("#data-grid-transpose-metadata-in-info-column")!;
+ expect(transposeSwitch().getAttribute("aria-checked")).toBe("false");
+ transposeSwitch().click();
+ await settle();
+ expect(host.querySelector("#data-grid-show-transpose-field-metadata")!.disabled).toBe(true);
+ expect(store.editorSettings.dataGridTransposeMetadataInInfoColumn).toBe(false);
+ clickText("settings.apply");
+ await settle();
+ expect(store.editorSettings.dataGridTransposeMetadataInInfoColumn).toBe(true);
+ expect(store.editorSettings.dataGridColumnCommentsInInfoArea).toBe(false);
+ expect(backend.saves).toHaveBeenCalledWith(expect.objectContaining({ dataGridTransposeMetadataInInfoColumn: true, dataGridColumnCommentsInInfoArea: false }));
+ page.value!.requestClose();
+ await settle();
+ open.value = true;
+ await settle();
+ expect(transposeSwitch().getAttribute("aria-checked")).toBe("true");
+ expect(host.querySelector("#column-comments-in-info-area")!.getAttribute("aria-checked")).toBe("false");
+ });
+
it("keeps a dirty draft when continuing, and closes without saving only after discarding", async () => {
const { page, open, store, changeFontSize } = await mountPage();
const originalFontSize = store.editorSettings.fontSize;
diff --git a/apps/desktop/src/components/grid/DataGrid.vue b/apps/desktop/src/components/grid/DataGrid.vue
index dade7eeaa8..c795f099a3 100644
--- a/apps/desktop/src/components/grid/DataGrid.vue
+++ b/apps/desktop/src/components/grid/DataGrid.vue
@@ -845,10 +845,13 @@ const dataGridTopbarOverflowCompact = ref(false);
const dataGridTopbarOverflowActionCount = ref(0);
const dataGridTopbarExpandedRequiredWidth = ref(0);
const dataGridFilterExpandedRequiredWidth = ref(0);
-const showColumnCommentsInHeader = computed(() => settingsStore.editorSettings.showColumnCommentsInHeader);
+const columnCommentsInInfoArea = computed(() => settingsStore.editorSettings.dataGridColumnCommentsInInfoArea);
+const showColumnCommentsInHeader = computed(() => settingsStore.editorSettings.showColumnCommentsInHeader && !columnCommentsInInfoArea.value);
const showColumnTypesInHeader = computed(() => settingsStore.editorSettings.showColumnTypesInHeader);
const showColumnHeaderTooltips = computed(() => settingsStore.editorSettings.showColumnHeaderTooltips !== false);
-const showTransposeFieldMetadata = computed(() => settingsStore.editorSettings.dataGridShowTransposeFieldMetadata);
+const transposeMetadataInInfoColumn = computed(() => settingsStore.editorSettings.dataGridTransposeMetadataInInfoColumn);
+const showTransposeFieldMetadata = computed(() => settingsStore.editorSettings.dataGridShowTransposeFieldMetadata && !transposeMetadataInInfoColumn.value);
+const showTransposeColumnComments = computed(() => settingsStore.editorSettings.showColumnCommentsInHeader);
const showIndexIndicatorsInHeader = computed(() => settingsStore.editorSettings.showIndexIndicatorsInHeader !== false);
const indexes = ref([]);
const indexesLoaded = ref(false);
@@ -6979,6 +6982,20 @@ const activeCellDetail = computed(() => {
return cell ? cellDetailFor(cell.rowIndex, cell.col) : null;
});
+function columnInfoText(actualColIdx: number, includeName = true): string {
+ const column = props.result.columns[actualColIdx];
+ if (column === undefined) return "";
+ const metadata = tableColumnForGridColumn(actualColIdx);
+ return [includeName ? column : undefined, allColumnTypes.value[actualColIdx], metadata ? `${t("structureEditor.nullable")}: ${t(metadata.is_nullable ? "structureEditor.yes" : "structureEditor.no")}` : undefined, resolvedColumnComment(column, actualColIdx)].filter(Boolean).join(" · ");
+}
+
+const selectedColumnInfo = computed(() => {
+ if (!columnCommentsInInfoArea.value || hasRowSelection.value) return "";
+ const visibleIndex = hasColumnSelection.value ? [...selectedColumnIndexes.value][0] : selectionFocus.value?.colIndex;
+ const actualIndex = visibleIndex === undefined ? undefined : visibleColumnIndexes.value[visibleIndex];
+ return actualIndex === undefined ? "" : columnInfoText(actualIndex);
+});
+
const canShowMongoJsonPreview = computed(() => props.databaseType === "mongodb" && !!props.result.mongo_documents && props.result.mongo_documents.length === props.result.rows.length);
const mongoJsonPreviewOpen = computed(() => showMongoJsonPreview.value && canShowMongoJsonPreview.value);
const activeMongoJsonDocument = computed(() => {
@@ -11472,6 +11489,7 @@ const transposePinnedWidth = computed(
}),
);
const transposeEndSpacerWidth = computed(() => {
+ if (transposeMetadataInInfoColumn.value) return 0;
if (!transposeScopeRecordIndexes.value.length) return 0;
return transposeEndAlignmentSpacerWidth({
viewportWidth: transposeViewportWidth.value,
@@ -11515,10 +11533,11 @@ const transposeRows = computed(() => {
});
});
const transposeReserveTypeLine = computed(() => showTransposeFieldMetadata.value && showColumnTypesInHeader.value && transposeRows.value.some((row) => row.type));
-const transposeReserveCommentLine = computed(() => showTransposeFieldMetadata.value && showColumnCommentsInHeader.value && transposeRows.value.some((row) => row.comment));
+const transposeReserveCommentLine = computed(() => showTransposeFieldMetadata.value && showTransposeColumnComments.value && transposeRows.value.some((row) => row.comment));
const transposeRowHeight = computed(() => 30 + (transposeReserveTypeLine.value ? 14 : 0) + (transposeReserveCommentLine.value ? 14 : 0));
const isTransposeMode = computed(() => showTranspose.value && transposeRows.value.length > 0);
-const transposeTotalWidth = computed(() => transposePinnedWidth.value + transposeRecordOffsets.value[transposeRecordOffsets.value.length - 1] + transposeEndSpacerWidth.value);
+const transposeInfoWidth = computed(() => (transposeMetadataInInfoColumn.value ? 360 : 0));
+const transposeTotalWidth = computed(() => transposePinnedWidth.value + transposeRecordOffsets.value[transposeRecordOffsets.value.length - 1] + transposeEndSpacerWidth.value + transposeInfoWidth.value);
function transposeScrollElement(): HTMLElement | undefined {
const raw = transposeScrollRef.value;
@@ -11529,7 +11548,7 @@ function transposeScrollElement(): HTMLElement | undefined {
function transposeFieldTitle(item: { column: string; type: string; comment?: string }): string {
const details = [item.column];
if (showTransposeFieldMetadata.value && showColumnTypesInHeader.value && item.type) details.push(`${t("grid.columnType")}: ${item.type}`);
- if (showTransposeFieldMetadata.value && showColumnCommentsInHeader.value && item.comment) details.push(`${t("grid.columnComment")}: ${item.comment}`);
+ if (showTransposeFieldMetadata.value && showTransposeColumnComments.value && item.comment) details.push(`${t("grid.columnComment")}: ${item.comment}`);
return details.join("\n");
}
@@ -14624,6 +14643,9 @@ useUpdateBlocker(() => (hasPendingChanges.value || hasPendingDataEditorDraft.val
+
@@ -14656,7 +14678,7 @@ useUpdateBlocker(() => (hasPendingChanges.value || hasPendingDataEditorDraft.val
{{ item.type }}
-
+
{{ item.comment }}
@@ -14673,7 +14695,7 @@ useUpdateBlocker(() => (hasPendingChanges.value || hasPendingDataEditorDraft.val
{{ t("grid.columnType") }}
{{ item.type }}
-
+
{{ t("grid.columnComment") }}
{{ item.comment }}
@@ -14839,6 +14861,9 @@ useUpdateBlocker(() => (hasPendingChanges.value || hasPendingDataEditorDraft.val
+
+ {{ columnInfoText(item.columnIndex, false) }}
+
@@ -16175,7 +16200,7 @@ useUpdateBlocker(() => (hasPendingChanges.value || hasPendingDataEditorDraft.val
-
+
{{
@@ -16214,7 +16239,10 @@ useUpdateBlocker(() => (hasPendingChanges.value || hasPendingDataEditorDraft.val
-
+
+ {{ selectedColumnInfo }}
+
+
diff --git a/apps/desktop/src/components/grid/__tests__/DataGridTransposeSort.spec.ts b/apps/desktop/src/components/grid/__tests__/DataGridTransposeSort.spec.ts
index 3c9e7bdaf0..4226a22c24 100644
--- a/apps/desktop/src/components/grid/__tests__/DataGridTransposeSort.spec.ts
+++ b/apps/desktop/src/components/grid/__tests__/DataGridTransposeSort.spec.ts
@@ -56,7 +56,7 @@ function transposeFieldNames(host: HTMLElement): string[] {
return [...host.querySelectorAll("[data-grid-transpose-column-index]")].map((field) => field.textContent?.trim().split("\n")[0] ?? "");
}
-function mountGrid() {
+function mountGrid(context: "table-data" | "results" = "table-data") {
const pinia = createPinia();
setActivePinia(pinia);
const settingsStore = useSettingsStore();
@@ -76,6 +76,7 @@ function mountGrid() {
execution_time_ms: 0,
}),
);
+ const resultColumnComments = ref>();
const host = document.createElement("div");
document.body.append(host);
@@ -89,13 +90,14 @@ function mountGrid() {
default: () =>
h(DataGrid, {
result: result.value,
+ resultColumnComments: resultColumnComments.value,
databaseType: "mysql",
- context: "table-data",
+ context,
tableMeta: {
tableName: "scores",
columns: [
- { name: "zeta", data_type: "varchar", is_nullable: false, column_default: null, is_primary_key: true, extra: null },
- { name: "alpha", data_type: "bigint", is_nullable: true, column_default: null, is_primary_key: false, extra: null },
+ { name: "zeta", data_type: "varchar", comment: "Display name with a long comment", is_nullable: false, column_default: null, is_primary_key: true, extra: null },
+ { name: "alpha", data_type: "bigint", comment: "Score", is_nullable: true, column_default: null, is_primary_key: false, extra: null },
],
primaryKeys: ["zeta"],
},
@@ -109,7 +111,7 @@ function mountGrid() {
app.use(i18n);
app.mount(host);
mountedApps.push({ app, host });
- return { host };
+ return { host, settingsStore, result, resultColumnComments };
}
async function openTranspose(host: HTMLElement) {
@@ -129,6 +131,89 @@ afterEach(() => {
});
describe("DataGrid transpose sorting", () => {
+ it.each(["table-data", "results"] as const)("moves all field metadata into an independent information column for %s", async (context) => {
+ const { host, settingsStore } = mountGrid(context);
+ settingsStore.updateEditorSettings({ dataGridShowTransposeFieldMetadata: true });
+ await settle();
+ await openTranspose(host);
+ expect(host.querySelectorAll("[data-grid-transpose-comment-line]")).toHaveLength(2);
+ expect(host.querySelector("[data-grid-transpose-column-info]")).toBeNull();
+
+ settingsStore.updateEditorSettings({ dataGridTransposeMetadataInInfoColumn: true });
+ await settle();
+ expect(host.querySelectorAll("[data-grid-transpose-comment-line]")).toHaveLength(0);
+ expect(host.querySelectorAll("[data-grid-transpose-type-line]")).toHaveLength(0);
+ const info = [...host.querySelectorAll("[data-grid-transpose-column-info]")];
+ expect(info).toHaveLength(2);
+ expect(info[0].textContent).toContain("Display name with a long comment");
+ expect(info[0].textContent).toContain("varchar");
+ expect(info[0].textContent).toContain("Nullable: No");
+ expect(info[0].textContent).not.toContain("zeta");
+ expect(info[0].title).toContain("Display name with a long comment");
+ expect(info[1].textContent).toContain("Score");
+ expect(info[1].textContent).toContain("Nullable: Yes");
+
+ settingsStore.updateEditorSettings({ dataGridTransposeMetadataInInfoColumn: false });
+ await settle();
+ expect(host.querySelectorAll("[data-grid-transpose-column-info]")).toHaveLength(0);
+ expect(host.querySelectorAll("[data-grid-transpose-comment-line]")).toHaveLength(2);
+ expect(host.querySelectorAll("[data-grid-transpose-type-line]")).toHaveLength(2);
+ });
+
+ it("keeps normal and transposed comment placement independent", async () => {
+ const { host, settingsStore } = mountGrid("results");
+ settingsStore.updateEditorSettings({ dataGridColumnCommentsInInfoArea: true, dataGridShowTransposeFieldMetadata: true });
+ await settle();
+ expect(host.querySelectorAll("[data-grid-header-comment-line]")).toHaveLength(0);
+ await openTranspose(host);
+ expect(host.querySelectorAll("[data-grid-transpose-column-info]")).toHaveLength(0);
+ expect(host.querySelectorAll("[data-grid-transpose-comment-line]")).toHaveLength(2);
+
+ settingsStore.updateEditorSettings({ dataGridColumnCommentsInInfoArea: false, dataGridTransposeMetadataInInfoColumn: true, dataGridShowTransposeFieldMetadata: false });
+ await settle();
+ expect(host.querySelectorAll("[data-grid-transpose-column-info]")).toHaveLength(2);
+ expect(host.querySelectorAll("[data-grid-transpose-type-line]")).toHaveLength(0);
+ expect(host.querySelectorAll("[data-grid-transpose-comment-line]")).toHaveLength(0);
+ });
+
+ it("uses ordinal query comments for duplicate names and clears stale metadata for a new result", async () => {
+ const { host, settingsStore, result, resultColumnComments } = mountGrid();
+ settingsStore.updateEditorSettings({ dataGridColumnCommentsInInfoArea: true });
+ result.value = { ...result.value, columns: ["name", "name"] };
+ resultColumnComments.value = ["First source", "Second source"];
+ await settle();
+ host.querySelector('[data-grid-column-index="1"]')!.click();
+ await settle();
+ expect(host.querySelector("[data-grid-column-info]")?.textContent).toContain("Second source");
+ expect(host.querySelector("[data-grid-column-info]")?.textContent).not.toContain("First source");
+
+ resultColumnComments.value = [undefined, undefined];
+ await settle();
+ expect(host.querySelector("[data-grid-column-info]")?.textContent).toContain("bigint");
+ expect(host.querySelector("[data-grid-column-info]")?.textContent).not.toContain("Second source");
+ expect(host.querySelector("[data-grid-column-info]")?.textContent).not.toContain("undefined");
+ });
+
+ it("shows selected column metadata in the status area and restores header comments when disabled", async () => {
+ const { host, settingsStore } = mountGrid();
+ await settle();
+ expect(host.querySelectorAll("[data-grid-header-comment-line]")).toHaveLength(2);
+ settingsStore.updateEditorSettings({ dataGridColumnCommentsInInfoArea: true });
+ await settle();
+ expect(host.querySelector("[data-grid-header-comment-line]")).toBeNull();
+ host.querySelector('[data-grid-column-index="0"]')!.click();
+ await settle();
+ expect(host.querySelector("[data-grid-column-info]")?.textContent).toContain("Display name with a long comment");
+ host.querySelector('[data-grid-column-index="1"]')!.click();
+ await settle();
+ expect(host.querySelector("[data-grid-column-info]")?.textContent).toContain("Score");
+ expect(host.querySelector("[data-grid-column-info]")?.textContent).toContain("bigint");
+ settingsStore.updateEditorSettings({ dataGridColumnCommentsInInfoArea: false });
+ await settle();
+ expect(host.querySelector("[data-grid-column-info]")).toBeNull();
+ expect(host.querySelectorAll("[data-grid-header-comment-line]")).toHaveLength(2);
+ });
+
it("sorts transpose field rows from the column-name header", async () => {
const { host } = mountGrid();
await settle();
diff --git a/apps/desktop/src/i18n/locales/en.ts b/apps/desktop/src/i18n/locales/en.ts
index 443472a33c..fab163babd 100644
--- a/apps/desktop/src/i18n/locales/en.ts
+++ b/apps/desktop/src/i18n/locales/en.ts
@@ -3222,6 +3222,7 @@ export default {
columnName: "Column",
columnType: "Type",
columnComment: "Comment",
+ columnInfo: "Column information",
rowNumber: "Row",
valueLength: "Length",
nullValue: "NULL",
@@ -9094,6 +9095,8 @@ export default {
webSqlFileUploadMaxMbDescription: "Largest SQL file the web server accepts for streaming execution ({min}–{max} MB).",
sqlFileUploadMaxMbSaved: "Web SQL file upload size saved.",
showColumnCommentsInHeader: "Show column comments under names",
+ dataGridColumnCommentsInInfoArea: "Show column comments in the information area",
+ dataGridColumnCommentsInInfoAreaDescription: "Show the selected column's comment in the bottom status bar instead of the grid header.",
showColumnCommentsInHeaderDescription: "Display table column comments directly below grid column names.",
showColumnTypesInHeader: "Show column types under names",
showColumnTypesInHeaderDescription: "Display each column's data type directly below grid column names.",
@@ -9109,6 +9112,8 @@ export default {
resultTabPreferComments: "Prefer SQL comments when available",
resultTabPreferCommentsDescription: "Use the comment above the SQL statement when available; turn off to always use the source table name.",
dataGridShowTransposeFieldMetadata: "Show field metadata in transpose view",
+ dataGridTransposeMetadataInInfoColumn: "Use a separate information column in transpose view",
+ dataGridTransposeMetadataInInfoColumnDescription: "Show column types, nullability, and comments beside values in transposed table data and query results. Field names stay on one line.",
dataGridShowTransposeFieldMetadataDescription: "Display enabled column types and comments on separate lines in transpose view. This increases row height.",
colorizeDataGridCellTypes: "Color-code data grid values",
colorizeDataGridCellTypesDescription: "Use subtle, theme-aware text colors to distinguish values by their column data type.",
diff --git a/apps/desktop/src/i18n/locales/es.ts b/apps/desktop/src/i18n/locales/es.ts
index 9ecc10dff2..bfaf9a37d8 100644
--- a/apps/desktop/src/i18n/locales/es.ts
+++ b/apps/desktop/src/i18n/locales/es.ts
@@ -2893,6 +2893,7 @@ export default withEnglishFallback({
columnName: "Columna",
columnType: "Tipo",
columnComment: "Comentario",
+ columnInfo: "Información de columna",
rowNumber: "Fila",
valueLength: "Longitud",
nullValue: "NULL",
@@ -8196,6 +8197,10 @@ export default withEnglishFallback({
webSqlFileUploadMaxMbDescription: "Archivo SQL más grande que el servidor web acepta para ejecución en streaming ({min}–{max} MB).",
sqlFileUploadMaxMbSaved: "Tamaño de carga de archivo SQL web guardado.",
showColumnCommentsInHeader: "Mostrar comentarios bajo los nombres",
+ dataGridColumnCommentsInInfoArea: "Mostrar comentarios de columnas en el área de información",
+ dataGridColumnCommentsInInfoAreaDescription: "Mover los comentarios de las columnas seleccionadas a la barra de estado inferior y dejar de mostrarlos en el encabezado.",
+ dataGridTransposeMetadataInInfoColumn: "Usar una columna de información independiente en la vista transpuesta",
+ dataGridTransposeMetadataInInfoColumnDescription: "En la vista transpuesta de datos de tabla y resultados de consultas, mostrar el tipo, la nulabilidad y los comentarios junto a los valores. Los nombres de campo permanecen en una sola línea.",
showColumnCommentsInHeaderDescription: "Muestra los comentarios de columnas directamente debajo del nombre de la columna.",
showColumnTypesInHeader: "Mostrar tipos de columna bajo los nombres",
showColumnTypesInHeaderDescription: "Muestra el tipo de dato de cada columna directamente debajo del nombre de la columna.",
diff --git a/apps/desktop/src/i18n/locales/it.ts b/apps/desktop/src/i18n/locales/it.ts
index 25a8749edf..41685e0eea 100644
--- a/apps/desktop/src/i18n/locales/it.ts
+++ b/apps/desktop/src/i18n/locales/it.ts
@@ -2788,6 +2788,7 @@ export default withEnglishFallback({
columnName: "Colonna",
columnType: "Tipo",
columnComment: "Commento",
+ columnInfo: "Informazioni sulla colonna",
rowNumber: "Riga",
valueLength: "Lunghezza",
nullValue: "NULL",
@@ -7975,6 +7976,10 @@ export default withEnglishFallback({
webSqlFileUploadMaxMbDescription: "File SQL più grande che il server web accetta per l'esecuzione in streaming ({min}–{max} MB).",
sqlFileUploadMaxMbSaved: "Dimensione di caricamento file SQL web salvata.",
showColumnCommentsInHeader: "Mostra i commenti delle colonne sotto i nomi",
+ dataGridColumnCommentsInInfoArea: "Mostra i commenti delle colonne nell'area informazioni",
+ dataGridColumnCommentsInInfoAreaDescription: "Mostra il commento della colonna selezionata nella barra di stato inferiore invece che nell'intestazione della griglia.",
+ dataGridTransposeMetadataInInfoColumn: "Usa una colonna informazioni separata nella vista trasposta",
+ dataGridTransposeMetadataInInfoColumnDescription: "Nella vista trasposta di dati tabellari e risultati delle query, mostra tipo, nullabilità e commenti accanto ai valori. I nomi dei campi restano su una riga.",
showColumnCommentsInHeaderDescription: "Visualizza i commenti delle colonne della tabella direttamente sotto i nomi delle colonne nella griglia.",
showColumnTypesInHeader: "Mostra i tipi di colonna sotto i nomi",
showColumnTypesInHeaderDescription: "Visualizza il tipo di dato di ciascuna colonna direttamente sotto i nomi delle colonne nella griglia.",
diff --git a/apps/desktop/src/i18n/locales/ja.ts b/apps/desktop/src/i18n/locales/ja.ts
index c3af9d64c4..74dd8c5be9 100644
--- a/apps/desktop/src/i18n/locales/ja.ts
+++ b/apps/desktop/src/i18n/locales/ja.ts
@@ -2792,6 +2792,7 @@ export default withEnglishFallback({
columnName: "列",
columnType: "データ型",
columnComment: "コメント",
+ columnInfo: "列情報",
rowNumber: "行",
valueLength: "長さ",
nullValue: "NULL",
@@ -7999,6 +8000,10 @@ export default withEnglishFallback({
webSqlFileUploadMaxMbDescription: "Webサーバーがストリーミング実行に受け付ける最大のSQLファイルサイズ({min}~{max} MB)。",
sqlFileUploadMaxMbSaved: "Web SQLファイルのアップロードサイズを保存しました。",
showColumnCommentsInHeader: "列名の下にコメントを表示",
+ dataGridColumnCommentsInInfoArea: "情報エリアに列コメントを表示",
+ dataGridColumnCommentsInInfoAreaDescription: "選択した列のコメントをグリッドヘッダーの代わりに下部のステータスバーに表示します。",
+ dataGridTransposeMetadataInInfoColumn: "転置ビューで独立した情報列を使用",
+ dataGridTransposeMetadataInInfoColumnDescription: "表データとクエリ結果の転置ビューで、型・NULL 許容・コメントを値の横に表示します。フィールド名は 1 行に保たれます。",
showColumnCommentsInHeaderDescription: "グリッド列名の直下にテーブル列コメントを表示します。",
showColumnTypesInHeader: "列名の下にデータ型を表示",
showColumnTypesInHeaderDescription: "グリッド列名の直下に各列のデータ型を表示します。",
diff --git a/apps/desktop/src/i18n/locales/ko.ts b/apps/desktop/src/i18n/locales/ko.ts
index 5be69d7d86..bcf61cd90b 100644
--- a/apps/desktop/src/i18n/locales/ko.ts
+++ b/apps/desktop/src/i18n/locales/ko.ts
@@ -2833,6 +2833,7 @@ export default withEnglishFallback({
columnName: "컬럼",
columnType: "타입",
columnComment: "주석",
+ columnInfo: "열 정보",
rowNumber: "행",
valueLength: "길이",
nullValue: "NULL",
@@ -7844,6 +7845,10 @@ export default withEnglishFallback({
webSqlFileUploadMaxMbDescription: "웹 서버가 스트리밍 실행에 허용하는 가장 큰 SQL 파일({min}–{max} MB).",
sqlFileUploadMaxMbSaved: "웹 SQL 파일 업로드 크기가 저장되었습니다.",
showColumnCommentsInHeader: "이름 아래에 컬럼 주석 표시",
+ dataGridColumnCommentsInInfoArea: "정보 영역에 열 주석 표시",
+ dataGridColumnCommentsInInfoAreaDescription: "선택한 열의 주석을 그리드 헤더 대신 하단 상태 표시줄에 표시합니다.",
+ dataGridTransposeMetadataInInfoColumn: "전치 보기에서 별도 정보 열 사용",
+ dataGridTransposeMetadataInInfoColumnDescription: "테이블 데이터 및 쿼리 결과의 전치 보기에서 유형, NULL 허용 여부, 주석을 값 옆에 표시합니다. 필드 이름은 한 줄로 유지됩니다.",
showColumnCommentsInHeaderDescription: "그리드 컬럼 이름 바로 아래에 테이블 컬럼 주석을 표시합니다.",
showColumnTypesInHeader: "이름 아래에 컬럼 타입 표시",
showColumnTypesInHeaderDescription: "그리드 컬럼 이름 바로 아래에 각 컬럼의 데이터 타입을 표시합니다.",
diff --git a/apps/desktop/src/i18n/locales/pt-BR.ts b/apps/desktop/src/i18n/locales/pt-BR.ts
index 2df2b5ff0e..d99ee86f75 100644
--- a/apps/desktop/src/i18n/locales/pt-BR.ts
+++ b/apps/desktop/src/i18n/locales/pt-BR.ts
@@ -2788,6 +2788,7 @@ export default withEnglishFallback({
columnName: "Coluna",
columnType: "Tipo",
columnComment: "Comentário",
+ columnInfo: "Informações da coluna",
rowNumber: "Linha",
valueLength: "Comprimento",
nullValue: "NULL",
@@ -7974,6 +7975,10 @@ export default withEnglishFallback({
webSqlFileUploadMaxMbDescription: "Maior arquivo SQL que o servidor web aceita para execução em streaming ({min}–{max} MB).",
sqlFileUploadMaxMbSaved: "Tamanho de upload de arquivo SQL web salvo.",
showColumnCommentsInHeader: "Mostrar comentários de coluna sob os nomes",
+ dataGridColumnCommentsInInfoArea: "Mostrar comentários de colunas na área de informações",
+ dataGridColumnCommentsInInfoAreaDescription: "Mostra o comentário da coluna selecionada na barra de status inferior em vez do cabeçalho da grade.",
+ dataGridTransposeMetadataInInfoColumn: "Usar uma coluna de informações separada na visão transposta",
+ dataGridTransposeMetadataInInfoColumnDescription: "Na visão transposta de dados de tabela e resultados de consultas, mostra tipos, nulidade e comentários ao lado dos valores. Os nomes dos campos permanecem em uma linha.",
showColumnCommentsInHeaderDescription: "Exibir comentários de colunas da tabela diretamente abaixo dos nomes das colunas da grade.",
showColumnTypesInHeader: "Mostrar tipos de coluna sob os nomes",
showColumnTypesInHeaderDescription: "Exibir o tipo de dado de cada coluna diretamente abaixo dos nomes das colunas da grade.",
diff --git a/apps/desktop/src/i18n/locales/ru.ts b/apps/desktop/src/i18n/locales/ru.ts
index 4b82a44065..666e84ed49 100644
--- a/apps/desktop/src/i18n/locales/ru.ts
+++ b/apps/desktop/src/i18n/locales/ru.ts
@@ -3152,6 +3152,7 @@ export default withEnglishFallback({
cut: "Вырезать",
uncommittedBlankRow: "Незаполненная новая строка будет проигнорирована при сохранении",
copyExtractorSqlDeletesInserts: "SQL-операторы DELETEs/INSERTs",
+ columnInfo: "Информация о столбце",
},
exportProgress: {
streamingUnsupported: "Потоковый экспорт не поддерживается для этого запроса. Упростите его или используйте поддерживаемый драйвер.",
@@ -10458,6 +10459,10 @@ export default withEnglishFallback({
shortcutSelectLineEnds: "Выделить концы строк",
openQueryOnConnectionOpen: "Открывать страницу запроса при открытии подключения",
openQueryOnConnectionOpenDescription: "При открытии подключения или базы данных открывается запрос default.sql. Изменения сохраняются автоматически.",
+ dataGridColumnCommentsInInfoArea: "Показывать комментарии к столбцам в информационной области",
+ dataGridColumnCommentsInInfoAreaDescription: "Переносить комментарии выбранных столбцов в нижнюю строку состояния и не отображать их в заголовке таблицы.",
+ dataGridTransposeMetadataInInfoColumn: "Отдельный информационный столбец в транспонированном представлении",
+ dataGridTransposeMetadataInInfoColumnDescription: "В транспонированном представлении данных таблицы и результатов запроса размещать тип, допустимость NULL и комментарии в отдельном информационном столбце справа от значений; имена полей остаются в одной строке.",
},
driverStore: {
jreDirRemoveFailed: "Не удалось удалить старый каталог JRE: {path} (исходная ошибка: {error})",
diff --git a/apps/desktop/src/i18n/locales/th.ts b/apps/desktop/src/i18n/locales/th.ts
index 96d9c1fcb1..a41b1b46ea 100644
--- a/apps/desktop/src/i18n/locales/th.ts
+++ b/apps/desktop/src/i18n/locales/th.ts
@@ -3376,6 +3376,7 @@ export default withEnglishFallback({
filterModeOrderReset: "คืนค่าลำดับเริ่มต้น",
uncommittedBlankRow: "แถวใหม่ที่ยังไม่ได้กรอกจะถูกละเว้นเมื่อบันทึก",
copyExtractorSqlDeletesInserts: "คำสั่ง SQL DELETEs/INSERTs",
+ columnInfo: "ข้อมูลคอลัมน์",
},
exportProgress: {
streamingUnsupported: "ไม่รองรับการส่งออกแบบ Streaming สำหรับ Query นี้ ทำให้เรียบง่ายขึ้นหรือใช้ Driver ที่รองรับ",
@@ -10888,6 +10889,10 @@ export default withEnglishFallback({
shortcutToggleCaseSelection: "สลับตัวพิมพ์ของการเลือก",
shortcutConvertNamingStyle: "สลับรูปแบบการตั้งชื่อ",
shortcutExPasteSqlInCondition: "ExPaste: วางเป็นเงื่อนไข IN",
+ dataGridColumnCommentsInInfoArea: "แสดงคำอธิบายคอลัมน์ในพื้นที่ข้อมูล",
+ dataGridColumnCommentsInInfoAreaDescription: "ย้ายคำอธิบายของคอลัมน์ที่เลือกไปยังแถบสถานะด้านล่าง และไม่แสดงในส่วนหัวอีกต่อไป",
+ dataGridTransposeMetadataInInfoColumn: "มุมมองทรานสโพสใช้คอลัมน์ข้อมูลแยกต่างหาก",
+ dataGridTransposeMetadataInInfoColumnDescription: "ในมุมมองทรานสโพสของข้อมูลตารางและผลลัพธ์คิวรี ให้วางประเภท การเป็นค่าว่างได้ และคำอธิบายไว้ในคอลัมน์ข้อมูลแยกทางด้านขวาของค่า โดยชื่อคอลัมน์คงอยู่ในบรรทัดเดียว",
},
driverStore: {
jreDirRemoveFailed: "ลบไดเรกทอรี JRE เก่าไม่สำเร็จ: {path} (ข้อผิดพลาดเดิม: {error})",
diff --git a/apps/desktop/src/i18n/locales/zh-CN.ts b/apps/desktop/src/i18n/locales/zh-CN.ts
index 0bfbd34605..d53a50b266 100644
--- a/apps/desktop/src/i18n/locales/zh-CN.ts
+++ b/apps/desktop/src/i18n/locales/zh-CN.ts
@@ -3156,6 +3156,7 @@ export default withEnglishFallback({
columnName: "列名",
columnType: "类型",
columnComment: "注释",
+ columnInfo: "字段信息",
rowNumber: "行号",
valueLength: "长度",
nullValue: "NULL",
@@ -9076,6 +9077,8 @@ export default withEnglishFallback({
webSqlFileUploadMaxMbDescription: "Web 服务器接受用于流式执行的最大 SQL 文件({min}–{max} MB)。",
sqlFileUploadMaxMbSaved: "Web SQL 文件上传大小已保存。",
showColumnCommentsInHeader: "在字段名下方显示注释",
+ dataGridColumnCommentsInInfoArea: "在信息区显示字段注释",
+ dataGridColumnCommentsInInfoAreaDescription: "将选中字段的注释移至底部状态栏,不再显示在表头中。",
showColumnCommentsInHeaderDescription: "把表字段注释直接显示在结果表头字段名下方。",
showColumnTypesInHeader: "在字段名下方显示数据类型",
showColumnTypesInHeaderDescription: "把每个字段的数据类型直接显示在结果表头字段名下方。",
@@ -9091,6 +9094,8 @@ export default withEnglishFallback({
resultTabPreferComments: "有注释时优先展示注释",
resultTabPreferCommentsDescription: "开启后优先使用 SQL 语句上方的注释;关闭后按来源表名显示。",
dataGridShowTransposeFieldMetadata: "在转置视图中显示字段元数据",
+ dataGridTransposeMetadataInInfoColumn: "转置视图使用独立信息列",
+ dataGridTransposeMetadataInInfoColumnDescription: "在表数据和查询结果的转置视图中,将类型、可空性和注释放在值右侧的独立信息列,字段名保持单行。",
dataGridShowTransposeFieldMetadataDescription: "在转置视图中分行显示已启用的数据类型和注释。开启后会增加行高。",
colorizeDataGridCellTypes: "按数据类型为表格值着色",
colorizeDataGridCellTypesDescription: "使用适配主题的克制文字颜色,按字段数据类型区分表格中的值。",
diff --git a/apps/desktop/src/i18n/locales/zh-TW.ts b/apps/desktop/src/i18n/locales/zh-TW.ts
index 11920f5370..8fb934b66f 100644
--- a/apps/desktop/src/i18n/locales/zh-TW.ts
+++ b/apps/desktop/src/i18n/locales/zh-TW.ts
@@ -2779,6 +2779,7 @@ export default withEnglishFallback({
columnName: "欄位名稱",
columnType: "類型",
columnComment: "註解",
+ columnInfo: "欄位資訊",
rowNumber: "行號",
valueLength: "長度",
nullValue: "NULL",
@@ -7318,6 +7319,8 @@ export default withEnglishFallback({
webSqlFileUploadMaxMbDescription: "Web 伺服器接受用於串流執行之最大 SQL 檔案({min}–{max} MB)。",
sqlFileUploadMaxMbSaved: "Web SQL 檔案上傳大小已儲存。",
showColumnCommentsInHeader: "在欄位名稱下方顯示註解",
+ dataGridColumnCommentsInInfoArea: "在資訊區顯示欄位註解",
+ dataGridColumnCommentsInInfoAreaDescription: "將選取欄位的註解移至底部狀態列,不再顯示於表頭中。",
showColumnCommentsInHeaderDescription: "直接在資料表格欄位名稱下方顯示資料表欄位註解。",
showColumnTypesInHeader: "在欄位名稱下方顯示資料類型",
showColumnTypesInHeaderDescription: "直接在資料表格欄位名稱下方顯示每個欄位的資料類型。",
@@ -7333,6 +7336,8 @@ export default withEnglishFallback({
resultTabPreferComments: "有註解時優先顯示註解",
resultTabPreferCommentsDescription: "開啟後優先使用 SQL 語句上方的註解;關閉後依來源資料表名稱顯示。",
dataGridShowTransposeFieldMetadata: "在轉置檢視中顯示欄位中繼資料",
+ dataGridTransposeMetadataInInfoColumn: "轉置檢視使用獨立資訊欄",
+ dataGridTransposeMetadataInInfoColumnDescription: "在資料表與查詢結果的轉置檢視中,將類型、可空性和註解放在值右側的獨立資訊欄,欄位名稱維持單行。",
dataGridShowTransposeFieldMetadataDescription: "在轉置檢視中分行顯示已啟用的資料類型和註解。開啟後會增加列高。",
colorizeDataGridCellTypes: "依資料類型為表格值著色",
colorizeDataGridCellTypesDescription: "使用配合主題的柔和文字顏色,依欄位資料類型區分表格中的值。",
diff --git a/apps/desktop/src/lib/settings/__tests__/editorSettingsDraft.spec.ts b/apps/desktop/src/lib/settings/__tests__/editorSettingsDraft.spec.ts
index ba625b730d..5c2d60336f 100644
--- a/apps/desktop/src/lib/settings/__tests__/editorSettingsDraft.spec.ts
+++ b/apps/desktop/src/lib/settings/__tests__/editorSettingsDraft.spec.ts
@@ -43,6 +43,12 @@ function makeSettings(overrides: Partial = {}): EditorSettings {
}
describe("EDITOR_SETTINGS_DRAFT_KEYS", () => {
+ it("captures and saves the column comment placement preference", () => {
+ const base = editorSettingsDraftFromSettings(makeSettings({ dataGridColumnCommentsInInfoArea: false, dataGridTransposeMetadataInInfoColumn: false }));
+ const draft = editorSettingsDraftFromSettings(makeSettings({ dataGridColumnCommentsInInfoArea: true, dataGridTransposeMetadataInInfoColumn: true }));
+ expect(draft.dataGridColumnCommentsInInfoArea).toBe(true);
+ expect(editorSettingsPatchFromDraft(draft, base)).toEqual({ dataGridColumnCommentsInInfoArea: true, dataGridTransposeMetadataInInfoColumn: true });
+ });
it("keeps connection and query timeout ownership outside editor settings", () => {
expect(EDITOR_SETTINGS_DRAFT_KEYS).not.toContain("globalConnectTimeoutSecs");
expect(EDITOR_SETTINGS_DRAFT_KEYS).not.toContain("globalQueryTimeoutSecs");
diff --git a/apps/desktop/src/lib/settings/__tests__/settingsTransfer.spec.ts b/apps/desktop/src/lib/settings/__tests__/settingsTransfer.spec.ts
index 32d1314a82..5e3df9a3d1 100644
--- a/apps/desktop/src/lib/settings/__tests__/settingsTransfer.spec.ts
+++ b/apps/desktop/src/lib/settings/__tests__/settingsTransfer.spec.ts
@@ -14,6 +14,17 @@ function fileWith(editor: Record, overrides: Record {
+ it("round-trips column comment placement as a data preference and rejects non-booleans", () => {
+ const text = serializeSettingsTransfer({ ...DEFAULT_EDITOR_SETTINGS, dataGridColumnCommentsInInfoArea: true, dataGridTransposeMetadataInInfoColumn: true });
+ const result = parseSettingsTransferFile(text);
+ expect(result.ok).toBe(true);
+ if (result.ok) expect(result.value.editorSettings.dataGridColumnCommentsInInfoArea).toBe(true);
+ if (result.ok) expect(result.value.editorSettings.dataGridTransposeMetadataInInfoColumn).toBe(true);
+ expect(transferCategoryForKey("dataGridTransposeMetadataInInfoColumn")).toBe("data");
+ expect(transferCategoryForKey("dataGridColumnCommentsInInfoArea")).toBe("data");
+ expect(parseSettingsTransferFile(fileWith({ dataGridColumnCommentsInInfoArea: "true" })).ok).toBe(false);
+ expect(parseSettingsTransferFile(fileWith({ dataGridTransposeMetadataInInfoColumn: "true" })).ok).toBe(false);
+ });
it("round-trips an opt-out of comment-first result names and rejects non-booleans", () => {
const text = serializeSettingsTransfer({ ...DEFAULT_EDITOR_SETTINGS, resultTabPreferComments: false });
const result = parseSettingsTransferFile(text);
diff --git a/apps/desktop/src/lib/settings/editorSettingsDraft.ts b/apps/desktop/src/lib/settings/editorSettingsDraft.ts
index 2f50d12d9c..cdf2484a04 100644
--- a/apps/desktop/src/lib/settings/editorSettingsDraft.ts
+++ b/apps/desktop/src/lib/settings/editorSettingsDraft.ts
@@ -61,12 +61,14 @@ export const EDITOR_SETTINGS_DRAFT_KEYS = [
"tabSortMode",
"tabMaxWidth",
"showColumnCommentsInHeader",
+ "dataGridColumnCommentsInInfoArea",
"showColumnTypesInHeader",
"showColumnHeaderTooltips",
"showResultSourceDatabase",
"resultTabNamingMode",
"resultTabPreferComments",
"dataGridShowTransposeFieldMetadata",
+ "dataGridTransposeMetadataInInfoColumn",
"colorizeDataGridCellTypes",
"dataGridTypeColorSchemes",
"activeDataGridTypeColorSchemeId",
diff --git a/apps/desktop/src/lib/settings/settingsSearch.ts b/apps/desktop/src/lib/settings/settingsSearch.ts
index be3cae9455..b29da793c8 100644
--- a/apps/desktop/src/lib/settings/settingsSearch.ts
+++ b/apps/desktop/src/lib/settings/settingsSearch.ts
@@ -241,12 +241,14 @@ export const SETTINGS_SEARCH_DEFINITIONS: readonly SettingsSearchDefinition[] =
{ id: "default-explain-view", category: "data", titleKey: "settings.defaultExplainView", descriptionKey: "settings.defaultExplainViewDescription", targetId: "default-explain-view" },
{ id: "query-result-max-rows", category: "data", titleKey: "settings.queryResultMaxRows", descriptionKey: "settings.queryResultMaxRowsDescription", targetId: "data" },
{ id: "data-grid-header-comments", category: "data", titleKey: "settings.showColumnCommentsInHeader", descriptionKey: "settings.showColumnCommentsInHeaderDescription", targetId: "data" },
+ { id: "data-grid-comment-info-area", category: "data", titleKey: "settings.dataGridColumnCommentsInInfoArea", descriptionKey: "settings.dataGridColumnCommentsInInfoAreaDescription", targetId: "data" },
{ id: "data-grid-header-types", category: "data", titleKey: "settings.showColumnTypesInHeader", descriptionKey: "settings.showColumnTypesInHeaderDescription", targetId: "data" },
{ id: "data-grid-header-tooltips", category: "data", titleKey: "settings.showColumnHeaderTooltips", descriptionKey: "settings.showColumnHeaderTooltipsDescription", targetId: "data" },
{ id: "result-source-database-name", category: "data", titleKey: "settings.showResultSourceDatabase", descriptionKey: "settings.showResultSourceDatabaseDescription", targetId: "data" },
{ id: "result-tab-naming-mode", category: "data", titleKey: "settings.resultTabNamingMode", descriptionKey: "settings.resultTabNamingModeDescription", targetId: "data" },
{ id: "result-tab-prefer-comments", category: "data", titleKey: "settings.resultTabPreferComments", descriptionKey: "settings.resultTabPreferCommentsDescription", targetId: "data" },
{ id: "data-grid-transpose-field-metadata", category: "data", titleKey: "settings.dataGridShowTransposeFieldMetadata", descriptionKey: "settings.dataGridShowTransposeFieldMetadataDescription", targetId: "data" },
+ { id: "data-grid-transpose-info-column", category: "data", titleKey: "settings.dataGridTransposeMetadataInInfoColumn", descriptionKey: "settings.dataGridTransposeMetadataInInfoColumnDescription", targetId: "data" },
{ id: "data-grid-cell-type-colors", category: "data", titleKey: "settings.colorizeDataGridCellTypes", descriptionKey: "settings.colorizeDataGridCellTypesDescription", targetId: "data" },
{ id: "data-grid-type-colors", category: "data", titleKey: "settings.dataGridTypeColorScheme", descriptionKey: "settings.dataGridTypeColorSchemeDescription", targetId: "data-grid-type-colors" },
{ id: "data-grid-index-indicators", category: "data", titleKey: "settings.showIndexIndicatorsInHeader", descriptionKey: "settings.showIndexIndicatorsInHeaderDescription", targetId: "data" },
diff --git a/apps/desktop/src/lib/settings/settingsTransfer.ts b/apps/desktop/src/lib/settings/settingsTransfer.ts
index 62cd2d7f62..cdfcb79b5d 100644
--- a/apps/desktop/src/lib/settings/settingsTransfer.ts
+++ b/apps/desktop/src/lib/settings/settingsTransfer.ts
@@ -155,12 +155,14 @@ const SETTINGS_TRANSFER_CATEGORY_KEYS: Record, exist
autoCalculateTotalRows: settings.autoCalculateTotalRows ?? DEFAULT_EDITOR_SETTINGS.autoCalculateTotalRows,
mongoViewMode: settings.mongoViewMode === "table" ? "table" : DEFAULT_EDITOR_SETTINGS.mongoViewMode,
showColumnCommentsInHeader: settings.showColumnCommentsInHeader ?? DEFAULT_EDITOR_SETTINGS.showColumnCommentsInHeader,
+ dataGridColumnCommentsInInfoArea: settings.dataGridColumnCommentsInInfoArea ?? DEFAULT_EDITOR_SETTINGS.dataGridColumnCommentsInInfoArea,
showColumnTypesInHeader: settings.showColumnTypesInHeader ?? DEFAULT_EDITOR_SETTINGS.showColumnTypesInHeader,
showColumnHeaderTooltips: settings.showColumnHeaderTooltips ?? DEFAULT_EDITOR_SETTINGS.showColumnHeaderTooltips,
showResultSourceDatabase: settings.showResultSourceDatabase ?? DEFAULT_EDITOR_SETTINGS.showResultSourceDatabase,
@@ -2088,6 +2093,7 @@ export function normalizeEditorSettings(settings: Partial, exist
resultTabNamingMode: normalizeResultTabNamingMode(settings.resultTabNamingMode),
resultTabPreferComments: settings.resultTabPreferComments !== false,
dataGridShowTransposeFieldMetadata: settings.dataGridShowTransposeFieldMetadata === true,
+ dataGridTransposeMetadataInInfoColumn: settings.dataGridTransposeMetadataInInfoColumn === true,
colorizeDataGridCellTypes: settings.colorizeDataGridCellTypes ?? DEFAULT_EDITOR_SETTINGS.colorizeDataGridCellTypes,
dataGridTypeColorSchemes,
activeDataGridTypeColorSchemeId: normalizeActiveDataGridTypeColorSchemeId(dataGridTypeColorSchemes, settings.activeDataGridTypeColorSchemeId),
@@ -2972,6 +2978,7 @@ export const useSettingsStore = defineStore("settings", () => {
if (partial.autoCalculateTotalRows !== undefined) editorSettings.value.autoCalculateTotalRows = partial.autoCalculateTotalRows === true;
if (partial.mongoViewMode !== undefined) editorSettings.value.mongoViewMode = partial.mongoViewMode;
if (partial.showColumnCommentsInHeader !== undefined) editorSettings.value.showColumnCommentsInHeader = partial.showColumnCommentsInHeader;
+ if (partial.dataGridColumnCommentsInInfoArea !== undefined) editorSettings.value.dataGridColumnCommentsInInfoArea = partial.dataGridColumnCommentsInInfoArea;
if (partial.showColumnTypesInHeader !== undefined) editorSettings.value.showColumnTypesInHeader = partial.showColumnTypesInHeader;
if (partial.showColumnHeaderTooltips !== undefined) editorSettings.value.showColumnHeaderTooltips = partial.showColumnHeaderTooltips;
if (partial.showResultSourceDatabase !== undefined) editorSettings.value.showResultSourceDatabase = partial.showResultSourceDatabase;
@@ -2983,6 +2990,7 @@ export const useSettingsStore = defineStore("settings", () => {
if (partial.resultTabNamingMode !== undefined) editorSettings.value.resultTabNamingMode = normalizeResultTabNamingMode(partial.resultTabNamingMode);
if (partial.resultTabPreferComments !== undefined) editorSettings.value.resultTabPreferComments = partial.resultTabPreferComments !== false;
if (partial.dataGridShowTransposeFieldMetadata !== undefined) editorSettings.value.dataGridShowTransposeFieldMetadata = partial.dataGridShowTransposeFieldMetadata === true;
+ if (partial.dataGridTransposeMetadataInInfoColumn !== undefined) editorSettings.value.dataGridTransposeMetadataInInfoColumn = partial.dataGridTransposeMetadataInInfoColumn === true;
if (partial.colorizeDataGridCellTypes !== undefined) editorSettings.value.colorizeDataGridCellTypes = partial.colorizeDataGridCellTypes === true;
if (partial.dataGridTypeColorSchemes !== undefined) {
editorSettings.value.dataGridTypeColorSchemes = normalizeDataGridTypeColorSchemes(partial.dataGridTypeColorSchemes);
diff --git a/packages/app-tests/settingsStore.test.ts b/packages/app-tests/settingsStore.test.ts
index 4a27cceb99..d2c20a2ef4 100644
--- a/packages/app-tests/settingsStore.test.ts
+++ b/packages/app-tests/settingsStore.test.ts
@@ -636,6 +636,10 @@ test("defaults sidebar horizontal scroll to off", () => {
});
test("defaults data grid header display settings", () => {
+ assert.equal(DEFAULT_EDITOR_SETTINGS.dataGridTransposeMetadataInInfoColumn, false);
+ assert.equal(normalizeEditorSettings({}).dataGridTransposeMetadataInInfoColumn, false);
+ assert.equal(DEFAULT_EDITOR_SETTINGS.dataGridColumnCommentsInInfoArea, false);
+ assert.equal(normalizeEditorSettings({}).dataGridColumnCommentsInInfoArea, false);
assert.equal(DEFAULT_EDITOR_SETTINGS.showColumnCommentsInHeader, true);
assert.equal(DEFAULT_EDITOR_SETTINGS.dataGridShowTransposeFieldMetadata, false);
assert.equal(DEFAULT_EDITOR_SETTINGS.compactColumnHeaderActions, true);
@@ -646,16 +650,34 @@ test("defaults data grid header display settings", () => {
test("keeps saved data grid header display settings", () => {
const settings = normalizeEditorSettings({
+ dataGridTransposeMetadataInInfoColumn: true,
+ dataGridColumnCommentsInInfoArea: true,
showColumnCommentsInHeader: true,
dataGridShowTransposeFieldMetadata: true,
compactColumnHeaderActions: false,
} as any);
assert.equal(settings.showColumnCommentsInHeader, true);
+ assert.equal(settings.dataGridTransposeMetadataInInfoColumn, true);
+ assert.equal(settings.dataGridColumnCommentsInInfoArea, true);
assert.equal(settings.dataGridShowTransposeFieldMetadata, true);
assert.equal(settings.compactColumnHeaderActions, false);
});
+test("updates column comment placement through the store", () => {
+ setActivePinia(createPinia());
+ const store = useSettingsStore();
+ store.updateEditorSettings({ dataGridColumnCommentsInInfoArea: true });
+ assert.equal(store.editorSettings.dataGridColumnCommentsInInfoArea, true);
+ store.updateEditorSettings({ dataGridColumnCommentsInInfoArea: false });
+ assert.equal(store.editorSettings.dataGridColumnCommentsInInfoArea, false);
+ store.updateEditorSettings({ dataGridTransposeMetadataInInfoColumn: true });
+ assert.equal(store.editorSettings.dataGridTransposeMetadataInInfoColumn, true);
+ assert.equal(store.editorSettings.dataGridColumnCommentsInInfoArea, false);
+ store.updateEditorSettings({ dataGridTransposeMetadataInInfoColumn: false });
+ assert.equal(store.editorSettings.dataGridTransposeMetadataInInfoColumn, false);
+});
+
test("defaults column header hover tooltips to on", () => {
// Existing installs have no persisted flag, so the grid must keep showing the
// header tooltip until the user opts out.