feat(tabs): add configurable data tab reuse modes

This commit is contained in:
t8y2 committed 2026-08-05 13:05:26 +08:00
1 parent 1a818563b7
commit d65d712089
26 files changed
+393 -98

No files matched your search

@@ -37,6 +37,7 @@ import {
type DesktopIconTheme,
type InterfaceLayout,
type DisconnectTabHandlingMode,
type DataTabReuseMode,
type OpenTabsRestoreMode,
type SidebarObjectInfoMode,
type SqlSemanticDiagnosticsMode,
@@ -383,12 +384,13 @@ const editingShortcutId = ref<ShortcutActionId | null>(null);
const editSidebarActivation = ref(settingsStore.editorSettings.sidebarActivation);
const editSidebarObjectDisplay = ref(settingsStore.editorSettings.sidebarObjectDisplay);
const sidebarObjectDisplayHelp = ref<"grouped" | "simple" | null>(null);
const dataTabReuseModeHelp = ref<DataTabReuseMode | null>(null);
const editRoutineSourceOpenMode = ref(settingsStore.editorSettings.routineSourceOpenMode);
const editSidebarTableSearchEnabled = ref(settingsStore.editorSettings.sidebarTableSearchEnabled);
const editAutoSelectActiveSidebarNode = ref(settingsStore.editorSettings.autoSelectActiveSidebarNode);
const editOpenTabsRestoreMode = ref<OpenTabsRestoreMode>(settingsStore.editorSettings.openTabsRestoreMode);
const editDisconnectTabHandlingMode = ref<DisconnectTabHandlingMode>(settingsStore.editorSettings.disconnectTabHandlingMode);
const editReuseDataTab = ref(settingsStore.editorSettings.reuseDataTab);
const editDataTabReuseMode = ref<DataTabReuseMode>(settingsStore.editorSettings.dataTabReuseMode);
const editPrefillNewQueryWithSelect = ref(settingsStore.editorSettings.prefillNewQueryWithSelect);
const editClickTableNavigationTarget = ref<ClickTableNavigationTarget>(settingsStore.editorSettings.clickTableNavigationTarget);
const editUpdateNotificationsEnabled = ref(settingsStore.editorSettings.updateNotificationsEnabled);
@@ -499,7 +501,7 @@ function currentEditorSettingsDraft(): EditorSettingsDraft {
autoSelectActiveSidebarNode: editAutoSelectActiveSidebarNode.value,
openTabsRestoreMode: editOpenTabsRestoreMode.value,
disconnectTabHandlingMode: editDisconnectTabHandlingMode.value,
reuseDataTab: editReuseDataTab.value,
dataTabReuseMode: editDataTabReuseMode.value,
prefillNewQueryWithSelect: editPrefillNewQueryWithSelect.value,
updateNotificationsEnabled: editUpdateNotificationsEnabled.value,
sidebarObjectInfoMode: editSidebarObjectInfoMode.value,
@@ -767,7 +769,7 @@ function syncEditorSettingsDraftFromStore() {
editAutoSelectActiveSidebarNode.value = settingsStore.editorSettings.autoSelectActiveSidebarNode;
editOpenTabsRestoreMode.value = settingsStore.editorSettings.openTabsRestoreMode;
editDisconnectTabHandlingMode.value = settingsStore.editorSettings.disconnectTabHandlingMode;
editReuseDataTab.value = settingsStore.editorSettings.reuseDataTab;
editDataTabReuseMode.value = settingsStore.editorSettings.dataTabReuseMode;
editPrefillNewQueryWithSelect.value = settingsStore.editorSettings.prefillNewQueryWithSelect;
editClickTableNavigationTarget.value = settingsStore.editorSettings.clickTableNavigationTarget;
editUpdateNotificationsEnabled.value = settingsStore.editorSettings.updateNotificationsEnabled;
@@ -974,7 +976,7 @@ function resetDefaultsForTab(tab: SettingsCategory) {
editAutoSelectActiveSidebarNode.value = DEFAULT_EDITOR_SETTINGS.autoSelectActiveSidebarNode;
editOpenTabsRestoreMode.value = DEFAULT_EDITOR_SETTINGS.openTabsRestoreMode;
editDisconnectTabHandlingMode.value = DEFAULT_EDITOR_SETTINGS.disconnectTabHandlingMode;
editReuseDataTab.value = DEFAULT_EDITOR_SETTINGS.reuseDataTab;
editDataTabReuseMode.value = DEFAULT_EDITOR_SETTINGS.dataTabReuseMode;
editPrefillNewQueryWithSelect.value = DEFAULT_EDITOR_SETTINGS.prefillNewQueryWithSelect;
editClickTableNavigationTarget.value = DEFAULT_EDITOR_SETTINGS.clickTableNavigationTarget;
editUpdateNotificationsEnabled.value = DEFAULT_EDITOR_SETTINGS.updateNotificationsEnabled;
@@ -1067,7 +1069,7 @@ function resetAllDefaults() {
editAutoSelectActiveSidebarNode.value = DEFAULT_EDITOR_SETTINGS.autoSelectActiveSidebarNode;
editOpenTabsRestoreMode.value = DEFAULT_EDITOR_SETTINGS.openTabsRestoreMode;
editDisconnectTabHandlingMode.value = DEFAULT_EDITOR_SETTINGS.disconnectTabHandlingMode;
editReuseDataTab.value = DEFAULT_EDITOR_SETTINGS.reuseDataTab;
editDataTabReuseMode.value = DEFAULT_EDITOR_SETTINGS.dataTabReuseMode;
editPrefillNewQueryWithSelect.value = DEFAULT_EDITOR_SETTINGS.prefillNewQueryWithSelect;
editUpdateNotificationsEnabled.value = DEFAULT_EDITOR_SETTINGS.updateNotificationsEnabled;
editSidebarObjectInfoMode.value = DEFAULT_EDITOR_SETTINGS.sidebarObjectInfoMode;
@@ -4434,14 +4436,66 @@ onUnmounted(() => {
</Button>
</div>
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-2">
<div class="flex items-center gap-2">
<Label for="reuse-data-tab">{{ t("settings.reuseDataTab") }}</Label>
<Label>{{ t("settings.reuseDataTab") }}</Label>
<HelpTooltip :label="t('settings.reuseDataTab')">
{{ t("settings.reuseDataTabDescription") }}
</HelpTooltip>
</div>
<Switch id="reuse-data-tab" v-model="editReuseDataTab" />
<div class="grid grid-cols-1 gap-2 sm:grid-cols-3">
<Button type="button" variant="outline" class="h-auto items-start justify-start border p-3" :class="editDataTabReuseMode === 'always-new' ? 'dbx-choice-selected' : ''" @click="editDataTabReuseMode = 'always-new'">
<div class="text-left">
<div class="flex items-center gap-2">
<div class="text-sm font-medium">{{ t("settings.dataTabReuseAlwaysNew") }}</div>
<Tooltip :open="dataTabReuseModeHelp === 'always-new'">
<TooltipTrigger as-child>
<span class="inline-flex shrink-0 cursor-help text-muted-foreground hover:text-foreground" @click.stop @pointerdown.stop @mouseenter="dataTabReuseModeHelp = 'always-new'" @mouseleave="dataTabReuseModeHelp = null">
<CircleHelp class="h-3.5 w-3.5" />
</span>
</TooltipTrigger>
<TooltipContent class="max-w-[320px] text-xs leading-relaxed" side="top" align="center" :side-offset="8">
{{ t("settings.dataTabReuseAlwaysNewDescription") }}
</TooltipContent>
</Tooltip>
</div>
</div>
</Button>
<Button type="button" variant="outline" class="h-auto items-start justify-start border p-3" :class="editDataTabReuseMode === 'same-table' ? 'dbx-choice-selected' : ''" @click="editDataTabReuseMode = 'same-table'">
<div class="text-left">
<div class="flex items-center gap-2">
<div class="text-sm font-medium">{{ t("settings.dataTabReuseSameTable") }}</div>
<Tooltip :open="dataTabReuseModeHelp === 'same-table'">
<TooltipTrigger as-child>
<span class="inline-flex shrink-0 cursor-help text-muted-foreground hover:text-foreground" @click.stop @pointerdown.stop @mouseenter="dataTabReuseModeHelp = 'same-table'" @mouseleave="dataTabReuseModeHelp = null">
<CircleHelp class="h-3.5 w-3.5" />
</span>
</TooltipTrigger>
<TooltipContent class="max-w-[320px] text-xs leading-relaxed" side="top" align="center" :side-offset="8">
{{ t("settings.dataTabReuseSameTableDescription") }}
</TooltipContent>
</Tooltip>
</div>
</div>
</Button>
<Button type="button" variant="outline" class="h-auto items-start justify-start border p-3" :class="editDataTabReuseMode === 'active-tab' ? 'dbx-choice-selected' : ''" @click="editDataTabReuseMode = 'active-tab'">
<div class="text-left">
<div class="flex items-center gap-2">
<div class="text-sm font-medium">{{ t("settings.dataTabReuseActiveTab") }}</div>
<Tooltip :open="dataTabReuseModeHelp === 'active-tab'">
<TooltipTrigger as-child>
<span class="inline-flex shrink-0 cursor-help text-muted-foreground hover:text-foreground" @click.stop @pointerdown.stop @mouseenter="dataTabReuseModeHelp = 'active-tab'" @mouseleave="dataTabReuseModeHelp = null">
<CircleHelp class="h-3.5 w-3.5" />
</span>
</TooltipTrigger>
<TooltipContent class="max-w-[320px] text-xs leading-relaxed" side="top" align="center" :side-offset="8">
{{ t("settings.dataTabReuseActiveTabDescription") }}
</TooltipContent>
</Tooltip>
</div>
</div>
</Button>
</div>
</div>
<div class="space-y-2">
<Label>{{ t("settings.sidebarObjectDisplay") }}</Label>
@@ -3054,6 +3054,16 @@ watch([pendingChangeCount, pendingChangesVersion], () => {
schedulePreviewRefresh();
});
watch(
pendingChangeCount,
(count) => {
if (props.context !== "table-data" || !props.cacheKey) return;
const tab = queryStore.tabs.find((item) => item.id === props.cacheKey);
if (tab?.mode === "data") tab.pendingDataChangeCount = count || undefined;
},
{ immediate: true },
);
const saveActionMode = computed(() =>
dataGridSaveActionMode({
pendingChangeCount: pendingChangeCount.value,
@@ -1075,7 +1075,7 @@ function activateDataTableFromDoubleClick() {
if (node.type !== "table" || !hasNodeDatabaseContext(node)) return;
const activation = settingsStore.editorSettings.sidebarActivation;
const existingSameTableTab = findExistingSameTableDataTab();
const action = dataTableDoubleClickAction(existingSameTableTab, activation, settingsStore.editorSettings.reuseDataTab);
const action = dataTableDoubleClickAction(existingSameTableTab, activation, settingsStore.editorSettings.dataTabReuseMode);
if (action === "none") return;
if (action === "open") {
openDataImmediately(node);