feat(settings): allow disabling click to pin result tabs

This commit is contained in:
Xsyl.way
2026-10-17 20:09:22 +08:00
committed by GitHub
parent 268829afb2
commit 7abdbf3eb0
18 changed files with 84 additions and 1 deletions
@@ -828,6 +828,7 @@ const editDataGridKeepFilterEditorExpanded = ref(settingsStore.editorSettings.da
const dataGridFilterViewPreviewExpanded = ref(true);
const editDataGridTextFilterPanelHeight = ref(settingsStore.editorSettings.dataGridTextFilterPanelHeight);
const editDefaultAutoKeepResults = ref(settingsStore.editorSettings.defaultAutoKeepResults);
const editPinResultTabOnClick = ref(settingsStore.editorSettings.pinResultTabOnClick);
const editMultiStatementDefaultView = ref<MultiStatementDefaultView>(settingsStore.editorSettings.multiStatementDefaultView);
const editDefaultExplainView = ref<DefaultExplainView>(settingsStore.editorSettings.defaultExplainView);
const editDataGridAutoTransposeSingleRow = ref(settingsStore.editorSettings.dataGridAutoTransposeSingleRow);
@@ -1206,6 +1207,7 @@ function currentEditorSettingsDraft(): EditorSettingsDraft {
dataGridKeepFilterEditorExpanded: editDataGridKeepFilterEditorExpanded.value,
dataGridTextFilterPanelHeight: editDataGridTextFilterPanelHeight.value,
defaultAutoKeepResults: editDefaultAutoKeepResults.value,
pinResultTabOnClick: editPinResultTabOnClick.value,
multiStatementDefaultView: editMultiStatementDefaultView.value,
defaultExplainView: editDefaultExplainView.value,
dataGridAutoTransposeSingleRow: editDataGridAutoTransposeSingleRow.value,
@@ -1897,6 +1899,7 @@ function syncEditorSettingsDraftFromStore() {
editDataGridKeepFilterEditorExpanded.value = settingsStore.editorSettings.dataGridKeepFilterEditorExpanded;
editDataGridTextFilterPanelHeight.value = settingsStore.editorSettings.dataGridTextFilterPanelHeight;
editDefaultAutoKeepResults.value = settingsStore.editorSettings.defaultAutoKeepResults;
editPinResultTabOnClick.value = settingsStore.editorSettings.pinResultTabOnClick;
editMultiStatementDefaultView.value = settingsStore.editorSettings.multiStatementDefaultView;
editDefaultExplainView.value = settingsStore.editorSettings.defaultExplainView;
editDataGridAutoTransposeSingleRow.value = settingsStore.editorSettings.dataGridAutoTransposeSingleRow;
@@ -2062,6 +2065,7 @@ const editorSettingsDraftRefs: EditorSettingsDraftRefMap = {
dataGridKeepFilterEditorExpanded: editDataGridKeepFilterEditorExpanded,
dataGridTextFilterPanelHeight: editDataGridTextFilterPanelHeight,
defaultAutoKeepResults: editDefaultAutoKeepResults,
pinResultTabOnClick: editPinResultTabOnClick,
multiStatementDefaultView: editMultiStatementDefaultView,
defaultExplainView: editDefaultExplainView,
dataGridAutoTransposeSingleRow: editDataGridAutoTransposeSingleRow,
@@ -2702,6 +2706,7 @@ function resetDefaultsForTab(tab: SettingsCategory) {
editDataGridKeepFilterEditorExpanded.value = DEFAULT_EDITOR_SETTINGS.dataGridKeepFilterEditorExpanded;
editDataGridTextFilterPanelHeight.value = DEFAULT_EDITOR_SETTINGS.dataGridTextFilterPanelHeight;
editDefaultAutoKeepResults.value = DEFAULT_EDITOR_SETTINGS.defaultAutoKeepResults;
editPinResultTabOnClick.value = DEFAULT_EDITOR_SETTINGS.pinResultTabOnClick;
editMultiStatementDefaultView.value = DEFAULT_EDITOR_SETTINGS.multiStatementDefaultView;
editDefaultExplainView.value = DEFAULT_EDITOR_SETTINGS.defaultExplainView;
editDataGridAutoTransposeSingleRow.value = DEFAULT_EDITOR_SETTINGS.dataGridAutoTransposeSingleRow;
@@ -2832,6 +2837,7 @@ function resetAllDefaults() {
editDataGridKeepFilterEditorExpanded.value = DEFAULT_EDITOR_SETTINGS.dataGridKeepFilterEditorExpanded;
editDataGridTextFilterPanelHeight.value = DEFAULT_EDITOR_SETTINGS.dataGridTextFilterPanelHeight;
editDefaultAutoKeepResults.value = DEFAULT_EDITOR_SETTINGS.defaultAutoKeepResults;
editPinResultTabOnClick.value = DEFAULT_EDITOR_SETTINGS.pinResultTabOnClick;
editMultiStatementDefaultView.value = DEFAULT_EDITOR_SETTINGS.multiStatementDefaultView;
editDefaultExplainView.value = DEFAULT_EDITOR_SETTINGS.defaultExplainView;
editDataGridAutoTransposeSingleRow.value = DEFAULT_EDITOR_SETTINGS.dataGridAutoTransposeSingleRow;
@@ -9599,6 +9605,13 @@ onUnmounted(() => {
</div>
<Switch id="default-auto-keep-results" v-model="editDefaultAutoKeepResults" :aria-label="t('settings.defaultAutoKeepResults')" />
</div>
<div data-settings-search-id="pin-result-tab-on-click" :class="['settings-item flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2', settingsSearchTargetClass('pin-result-tab-on-click')]">
<div class="min-w-0 space-y-1">
<Label for="pin-result-tab-on-click">{{ t("settings.pinResultTabOnClick") }}</Label>
<p class="text-xs text-muted-foreground">{{ t("settings.pinResultTabOnClickDescription") }}</p>
</div>
<Switch id="pin-result-tab-on-click" v-model="editPinResultTabOnClick" :aria-label="t('settings.pinResultTabOnClick')" />
</div>
<div data-settings-search-id="multi-statement-default-view" :class="['settings-item flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2', settingsSearchTargetClass('multi-statement-default-view')]">
<div class="min-w-0 space-y-1">
<Label for="multi-statement-default-view">{{ t("settings.multiStatementDefaultView") }}</Label>
@@ -16,6 +16,7 @@ import { batchSqlRecoveryState, type BatchSqlRecoveryAction } from "@/lib/query/
import type { CSSProperties } from "vue";
import { useI18n } from "vue-i18n";
import { provideTabUiState } from "@/lib/tabs/tabUiState";
import { shouldPinResultRunOnClick } from "@/lib/tabs/resultRunPin";
import {
Check,
CheckSquare2,
@@ -1674,7 +1675,7 @@ function dispatchBeforeResultTabSwitch() {
async function selectResultRunFromTab(runId: string) {
if (!(await selectResultRun(runId))) return;
const run = resultRuns.value.find((item) => item.id === runId);
if (run && !run.pinned) toggleResultRunPinned(runId);
if (shouldPinResultRunOnClick(run, settingsStore.editorSettings.pinResultTabOnClick)) toggleResultRunPinned(runId);
}
async function focusResultRunByIndex(index: number) {
+2
View File
@@ -9068,6 +9068,8 @@ export default {
queryPageSizeDescription: "Rows loaded per query result page. The maximum supported page size is {max}.",
defaultAutoKeepResults: "Auto-keep query results by default",
defaultAutoKeepResultsDescription: "Applying this setting updates open query tabs and sets the default for new ones. You can still toggle it per tab.",
pinResultTabOnClick: "Pin a result tab when clicked",
pinResultTabOnClickDescription: "Clicking a result tab keeps that result when the next query runs. Turn this off if every execution should replace the current result; you can still pin results manually from the result tab menu.",
multiStatementDefaultView: "Default view for multiple statements",
multiStatementDefaultViewDescription: "Choose whether batches with multiple SQL statements open in the result table or execution summary.",
defaultExplainView: "Default view for execution plan",
+2
View File
@@ -8173,6 +8173,8 @@ export default withEnglishFallback({
queryPageSizeDescription: "Filas cargadas por página de resultados. Se admiten hasta {max} filas.",
defaultAutoKeepResults: "Conservar resultados automáticamente por defecto",
defaultAutoKeepResultsDescription: "Al aplicar este ajuste se actualizan las pestañas de consulta abiertas y se establece el valor predeterminado para las nuevas. Puedes cambiarlo en cada pestaña.",
pinResultTabOnClick: "Fijar la pestaña de resultado al hacer clic",
pinResultTabOnClickDescription: "Al hacer clic en una pestaña de resultado, ese resultado se conserva cuando se ejecuta la siguiente consulta. Desactívalo si cada ejecución debe reemplazar el resultado actual; aún puedes fijar resultados manualmente desde el menú de la pestaña.",
multiStatementDefaultView: "Vista predeterminada para varias sentencias",
multiStatementDefaultViewDescription: "Elige si los lotes con varias sentencias SQL se abren en la tabla de resultados o en el resumen de ejecución.",
defaultExplainView: "Vista predeterminada para el plan de ejecución",
+2
View File
@@ -7952,6 +7952,8 @@ export default withEnglishFallback({
queryPageSizeDescription: "Righe caricate per pagina dei risultati. Sono supportate fino a {max} righe.",
defaultAutoKeepResults: "Mantieni automaticamente i risultati per impostazione predefinita",
defaultAutoKeepResultsDescription: "Applicando questa impostazione si aggiornano le schede di query aperte e si imposta il valore predefinito per le nuove. Puoi modificarlo per ogni scheda.",
pinResultTabOnClick: "Fissa la scheda risultato al clic",
pinResultTabOnClickDescription: "Facendo clic su una scheda risultato, quel risultato viene conservato alla successiva esecuzione. Disattivalo se ogni esecuzione deve sostituire il risultato corrente; puoi comunque fissare i risultati manualmente dal menu della scheda.",
multiStatementDefaultView: "Vista predefinita per istruzioni multiple",
multiStatementDefaultViewDescription: "Scegli se i batch con più istruzioni SQL si aprono nella tabella dei risultati o nel riepilogo dell'esecuzione.",
defaultExplainView: "Vista predefinita per il piano di esecuzione",
+2
View File
@@ -7976,6 +7976,8 @@ export default withEnglishFallback({
queryPageSizeDescription: "クエリ結果の 1 ページあたりの行数です。最大 {max} 行まで指定できます。",
defaultAutoKeepResults: "クエリ結果を既定で自動保持",
defaultAutoKeepResultsDescription: "適用すると開いているクエリタブを更新し、新しいタブの既定値にも設定します。タブごとに切り替えられます。",
pinResultTabOnClick: "結果タブをクリック時に固定",
pinResultTabOnClickDescription: "結果タブをクリックすると、その結果を次のクエリ実行後も保持します。毎回の実行で現在の結果を置き換えたい場合はオフにしてください。結果タブのメニューから手動で固定することもできます。",
multiStatementDefaultView: "複数ステートメントの既定ビュー",
multiStatementDefaultViewDescription: "複数の SQL ステートメントを含むバッチを、結果テーブルまたは実行サマリーのどちらで開くか選択します。",
defaultExplainView: "実行計画の既定ビュー",
+2
View File
@@ -7819,6 +7819,8 @@ export default withEnglishFallback({
queryPageSizeDescription: "쿼리 결과 페이지당 로드할 행 수입니다. 최대 {max}행을 지원합니다.",
defaultAutoKeepResults: "기본적으로 쿼리 결과 자동 보관",
defaultAutoKeepResultsDescription: "적용하면 열린 쿼리 탭을 업데이트하고 새 탭의 기본값으로 설정합니다. 탭별로 전환할 수 있습니다.",
pinResultTabOnClick: "결과 탭 클릭 시 고정",
pinResultTabOnClickDescription: "결과 탭을 클릭하면 다음 쿼리 실행 후에도 해당 결과를 유지합니다. 매번 실행 시 현재 결과를 교체하려면 끄세요. 결과 탭 메뉴에서 수동으로 고정할 수도 있습니다.",
multiStatementDefaultView: "여러 구문 기본 보기",
multiStatementDefaultViewDescription: "여러 SQL 구문이 있는 배치를 결과 테이블 또는 실행 요약 중 어디에서 열지 선택합니다.",
defaultExplainView: "실행 계획 기본 보기",
+2
View File
@@ -7951,6 +7951,8 @@ export default withEnglishFallback({
queryPageSizeDescription: "Linhas carregadas por página de resultados. São suportadas até {max} linhas.",
defaultAutoKeepResults: "Manter resultados automaticamente por padrão",
defaultAutoKeepResultsDescription: "Ao aplicar esta configuração, as abas de consulta abertas são atualizadas e o padrão para novas abas é definido. Você pode alternar em cada aba.",
pinResultTabOnClick: "Fixar a aba de resultado ao clicar",
pinResultTabOnClickDescription: "Ao clicar em uma aba de resultado, esse resultado é mantido na próxima execução. Desative se cada execução deve substituir o resultado atual; ainda é possível fixar resultados manualmente pelo menu da aba.",
multiStatementDefaultView: "Visualização padrão para várias instruções",
multiStatementDefaultViewDescription: "Escolha se lotes com várias instruções SQL abrem na tabela de resultados ou no resumo da execução.",
defaultExplainView: "Visualização padrão para plano de execução",
+2
View File
@@ -9277,6 +9277,8 @@ export default withEnglishFallback({
queryPageSizeDescription: "Строк загружается на страницу результата запроса. Максимальный поддерживаемый размер страницы — {max}.",
defaultAutoKeepResults: "Автосохранение результатов запроса по умолчанию",
defaultAutoKeepResultsDescription: "Применение этой настройки обновляет открытые вкладки запросов и задаёт значение по умолчанию для новых. Переключать её для каждой вкладки всё равно можно.",
pinResultTabOnClick: "Закреплять вкладку результата при щелчке",
pinResultTabOnClickDescription: "При щелчке по вкладке результата он сохраняется после следующего запроса. Отключите, если каждый запуск должен заменять текущий результат; закрепить вручную можно в меню вкладки результата.",
multiStatementDefaultView: "Вид по умолчанию для нескольких операторов",
multiStatementDefaultViewDescription: "Выберите, открывать пакеты с несколькими SQL-операторами в таблице результатов или в сводке выполнения.",
defaultExplainView: "Вид по умолчанию для плана выполнения",
+2
View File
@@ -9724,6 +9724,8 @@ export default withEnglishFallback({
queryPageSizeDescription: "จำนวนแถวที่โหลดต่อหน้าผลลัพธ์ Query ขนาดหน้าสูงสุดที่รองรับคือ {max}",
defaultAutoKeepResults: "เก็บผลลัพธ์ Query อัตโนมัติโดยค่าเริ่มต้น",
defaultAutoKeepResultsDescription: "การใช้การตั้งค่านี้จะอัปเดตแท็บ Query ที่เปิดอยู่และกำหนดค่าเริ่มต้นสำหรับแท็บใหม่ คุณยังสลับเป็นรายแท็บได้",
pinResultTabOnClick: "ปักหมุดแท็บผลลัพธ์เมื่อคลิก",
pinResultTabOnClickDescription: "เมื่อคลิกแท็บผลลัพธ์ ผลลัพธ์นั้นจะถูกเก็บไว้เมื่อรันคิวรีครั้งถัดไป ปิดตัวเลือกนี้หากต้องการให้ทุกครั้งแทนที่ผลลัพธ์ปัจจุบัน ยังปักหมุดเองได้จากเมนูแท็บผลลัพธ์",
multiStatementDefaultView: "มุมมองเริ่มต้นสำหรับหลาย Statement",
multiStatementDefaultViewDescription: "เลือกว่าจะเปิด Batch ที่มีหลาย SQL Statement ในตารางผลลัพธ์หรือสรุปการเรียกใช้",
defaultExplainView: "มุมมองเริ่มต้นของ Execution Plan",
+2
View File
@@ -9050,6 +9050,8 @@ export default withEnglishFallback({
queryPageSizeDescription: "每个查询结果分页加载的行数,最大支持 {max} 行。",
defaultAutoKeepResults: "默认自动保留查询结果",
defaultAutoKeepResultsDescription: "应用后同步更新已打开的查询标签,新建查询标签也使用此设置。仍可在标签内单独切换。",
pinResultTabOnClick: "单击结果标签时固定结果",
pinResultTabOnClickDescription: "开启后,点击结果标签会同时固定该结果,下一次查询不会覆盖它;关闭后每次执行都直接替换当前结果,需要保留时可在结果标签菜单里手动固定。",
multiStatementDefaultView: "多语句执行默认视图",
multiStatementDefaultViewDescription: "选择多条 SQL 语句批量执行后默认打开数据表或执行摘要。",
defaultExplainView: "执行计划默认视图",
+2
View File
@@ -7295,6 +7295,8 @@ export default withEnglishFallback({
queryPageSizeDescription: "每個查詢結果分頁載入的列數,最多支援 {max} 列。",
defaultAutoKeepResults: "預設自動保留查詢結果",
defaultAutoKeepResultsDescription: "套用後同步更新已開啟的查詢分頁,新建查詢分頁也使用此設定。仍可在分頁內個別切換。",
pinResultTabOnClick: "點擊結果分頁時固定結果",
pinResultTabOnClickDescription: "開啟後,點擊結果分頁會同時固定該結果,下一次查詢不會覆蓋它;關閉後每次執行都直接取代目前結果,需要保留時可在結果分頁選單裡手動固定。",
multiStatementDefaultView: "多語句執行預設檢視",
multiStatementDefaultViewDescription: "選擇多條 SQL 語句批次執行後預設開啟資料表或執行摘要。",
defaultExplainView: "執行計畫預設檢視",
@@ -0,0 +1,21 @@
import { describe, expect, it } from "vitest";
import { shouldPinResultRunOnClick } from "@/lib/tabs/resultRunPin";
describe("shouldPinResultRunOnClick", () => {
it("pins an unpinned run while the option is enabled", () => {
expect(shouldPinResultRunOnClick({ pinned: false }, true)).toBe(true);
expect(shouldPinResultRunOnClick({}, true)).toBe(true);
});
it("leaves an already pinned run alone", () => {
expect(shouldPinResultRunOnClick({ pinned: true }, true)).toBe(false);
});
it("does not pin when the user disabled click-to-pin", () => {
expect(shouldPinResultRunOnClick({ pinned: false }, false)).toBe(false);
});
it("ignores a missing run", () => {
expect(shouldPinResultRunOnClick(undefined, true)).toBe(false);
});
});
@@ -76,6 +76,7 @@ export const EDITOR_SETTINGS_DRAFT_KEYS = [
"dataGridKeepFilterEditorExpanded",
"dataGridTextFilterPanelHeight",
"defaultAutoKeepResults",
"pinResultTabOnClick",
"multiStatementDefaultView",
"defaultExplainView",
"dataGridAutoTransposeSingleRow",
@@ -170,6 +170,7 @@ const SETTINGS_TRANSFER_CATEGORY_KEYS: Record<SettingsTransferCategoryId, readon
"dataGridTextFilterPanelHeight",
"dataGridToolbarLayout",
"defaultAutoKeepResults",
"pinResultTabOnClick",
"multiStatementDefaultView",
"defaultExplainView",
"dataGridAutoTransposeSingleRow",
@@ -0,0 +1,8 @@
/**
* Whether clicking a result-run tab should pin that run. Pinning on click keeps
* the result from being replaced by the next query (#9975), but users who always
* want the latest result can disable it (#12381).
*/
export function shouldPinResultRunOnClick(run: { pinned?: boolean } | null | undefined, pinOnClick: boolean): boolean {
return !!run && !run.pinned && pinOnClick !== false;
}
+14
View File
@@ -1058,6 +1058,20 @@ describe("normalizeEditorSettings - continueOnErrorOnBatch", () => {
});
});
describe("normalizeEditorSettings - pinResultTabOnClick", () => {
it("defaults pinResultTabOnClick to true", () => {
expect(normalizeEditorSettings({}).pinResultTabOnClick).toBe(true);
});
it("preserves an explicitly disabled value", () => {
expect(normalizeEditorSettings({ pinResultTabOnClick: false }).pinResultTabOnClick).toBe(false);
});
it("treats non-boolean values as enabled", () => {
expect(normalizeEditorSettings({ pinResultTabOnClick: "no" } as any).pinResultTabOnClick).toBe(true);
});
});
describe("normalizeEditorSettings - clickTableNavigationTarget", () => {
it("defaults clickTableNavigationTarget to 'data'", () => {
expect(normalizeEditorSettings({}).clickTableNavigationTarget).toBe("data");
+4
View File
@@ -1093,6 +1093,7 @@ export interface EditorSettings {
dataGridExtractorOptionsMigrationVersion: number;
resultRunDisplayMode: ResultRunDisplayMode;
defaultAutoKeepResults: boolean;
pinResultTabOnClick: boolean;
multiStatementDefaultView: MultiStatementDefaultView;
defaultExplainView: DefaultExplainView;
dataGridAutoTransposeSingleRow: boolean;
@@ -1436,6 +1437,7 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
dataGridExtractorOptionsMigrationVersion: DATA_GRID_EXTRACTOR_OPTIONS_MIGRATION_VERSION,
resultRunDisplayMode: "tabs",
defaultAutoKeepResults: false,
pinResultTabOnClick: true,
multiStatementDefaultView: "result",
defaultExplainView: "canvas",
dataGridAutoTransposeSingleRow: false,
@@ -2096,6 +2098,7 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
dataGridExtractorOptionsMigrationVersion: DATA_GRID_EXTRACTOR_OPTIONS_MIGRATION_VERSION,
resultRunDisplayMode: normalizeResultRunDisplayMode(settings.resultRunDisplayMode),
defaultAutoKeepResults: settings.defaultAutoKeepResults === true,
pinResultTabOnClick: settings.pinResultTabOnClick !== false,
multiStatementDefaultView: normalizeMultiStatementDefaultView(settings.multiStatementDefaultView),
defaultExplainView: normalizeDefaultExplainView(settings.defaultExplainView),
dataGridAutoTransposeSingleRow: settings.dataGridAutoTransposeSingleRow === true,
@@ -2996,6 +2999,7 @@ export const useSettingsStore = defineStore("settings", () => {
if (partial.dataGridExtractorOptionsMigrationVersion !== undefined) editorSettings.value.dataGridExtractorOptionsMigrationVersion = DATA_GRID_EXTRACTOR_OPTIONS_MIGRATION_VERSION;
if (partial.resultRunDisplayMode !== undefined) editorSettings.value.resultRunDisplayMode = normalizeResultRunDisplayMode(partial.resultRunDisplayMode);
if (partial.defaultAutoKeepResults !== undefined) editorSettings.value.defaultAutoKeepResults = partial.defaultAutoKeepResults === true;
if (partial.pinResultTabOnClick !== undefined) editorSettings.value.pinResultTabOnClick = partial.pinResultTabOnClick !== false;
if (partial.multiStatementDefaultView !== undefined) editorSettings.value.multiStatementDefaultView = normalizeMultiStatementDefaultView(partial.multiStatementDefaultView);
if (partial.defaultExplainView !== undefined) editorSettings.value.defaultExplainView = normalizeDefaultExplainView(partial.defaultExplainView);
if (partial.dataGridAutoTransposeSingleRow !== undefined) editorSettings.value.dataGridAutoTransposeSingleRow = partial.dataGridAutoTransposeSingleRow === true;