feat(settings): move settings into main tab

This commit is contained in:
t8y2
2026-07-01 14:06:40 +08:00
parent 5cd1320011
commit 00b5715307
5 changed files with 187 additions and 76 deletions
+67 -27
View File
@@ -88,6 +88,7 @@ const AiAssistant = defineAsyncComponent(() => import("@/components/editor/AiAss
const QueryHistory = defineAsyncComponent(() => import("@/components/editor/QueryHistory.vue"));
const SqlLibraryPanel = defineAsyncComponent(() => import("@/components/layout/SqlLibraryPanel.vue"));
const DriverStorePage = defineAsyncComponent(() => import("@/components/config/DriverStoreDialog.vue"));
const EditorSettingsPage = defineAsyncComponent(() => import("@/components/editor/EditorSettingsDialog.vue"));
const UpdateDialog = defineAsyncComponent(() => import("@/components/layout/UpdateDialog.vue"));
const CloseActionPromptDialog = defineAsyncComponent(() => import("@/components/layout/CloseActionPromptDialog.vue"));
const LoginPage = defineAsyncComponent(() => import("@/components/auth/LoginPage.vue"));
@@ -117,13 +118,16 @@ const setupRequired = ref(false);
const showConnectionDialog = ref(false);
const connectionDialogPrefill = ref<ConnectionDeepLinkDraft | null>(null);
const connectionDialogInitialTab = ref<ConfigTab | undefined>(undefined);
const showSettingsDialog = ref(false);
const settingsPageTabOpen = ref(false);
const settingsPageActive = ref(false);
const settingsInitialTab = ref("editor");
const settingsInitialSection = ref<string | undefined>(undefined);
const showQueryEditorDdlDialog = ref(false);
const driverStoreTabOpen = ref(false);
const driverStoreActive = ref(false);
const settingsReturnSurface = ref<"query" | "driverStore" | "welcome">("welcome");
const showDriverStore = computed(() => driverStoreTabOpen.value && driverStoreActive.value);
const showSettingsPage = computed(() => settingsPageTabOpen.value && settingsPageActive.value);
const showQuickOpen = ref(false);
const agentDriverUpdateCount = ref(0);
const showHistory = ref(false);
@@ -259,7 +263,37 @@ const updateNotificationsEnabled = computed(() => settingsStore.editorSettings.u
function openSettings(initialTab = "editor", initialSection?: string) {
settingsInitialTab.value = initialTab;
settingsInitialSection.value = initialSection;
showSettingsDialog.value = true;
if (!settingsPageActive.value) {
settingsReturnSurface.value = showDriverStore.value ? "driverStore" : activeTab.value ? "query" : "welcome";
}
activateSettingsPage();
}
function activateSettingsPage() {
settingsPageTabOpen.value = true;
settingsPageActive.value = true;
driverStoreActive.value = false;
}
function closeSettingsPage() {
settingsPageTabOpen.value = false;
settingsPageActive.value = false;
if (settingsReturnSurface.value === "driverStore" && driverStoreTabOpen.value) {
driverStoreActive.value = true;
return;
}
driverStoreActive.value = false;
}
function openDriverStorePage() {
driverStoreTabOpen.value = true;
driverStoreActive.value = true;
settingsPageActive.value = false;
}
function closeDriverStorePage() {
driverStoreTabOpen.value = false;
driverStoreActive.value = false;
}
const toolbarAgentDriverUpdateCount = computed(() => (updateNotificationsEnabled.value ? agentDriverUpdateCount.value : 0));
const toolbarHasUpdateAvailable = computed(() => updateNotificationsEnabled.value && hasUpdateAvailable.value);
@@ -369,6 +403,7 @@ watch(
}
if (id) newQueryContextSource.value = "tab";
if (id && driverStoreActive.value) driverStoreActive.value = false;
if (id && settingsPageActive.value) settingsPageActive.value = false;
selectedSql.value = "";
activeOutputView.value = "result";
if (id) queryStore.reloadEvictedTab(id);
@@ -1186,6 +1221,7 @@ function activateQueryTab(tabId: string): boolean {
dispatchBeforeTabSwitch(tabId);
queryStore.activeTabId = tabId;
driverStoreActive.value = false;
settingsPageActive.value = false;
return true;
}
@@ -1269,9 +1305,10 @@ function handleKeydown(e: KeyboardEvent) {
}
if (isCloseTabShortcut(e, shortcuts)) {
e.preventDefault();
if (showDriverStore.value) {
driverStoreTabOpen.value = false;
driverStoreActive.value = false;
if (showSettingsPage.value) {
closeSettingsPage();
} else if (showDriverStore.value) {
closeDriverStorePage();
} else if (queryStore.activeTabId) {
queryStore.closeTab(queryStore.activeTabId);
}
@@ -1401,8 +1438,7 @@ function handleContextMenu(e: MouseEvent) {
}
function openDriverStoreFromEvent() {
driverStoreTabOpen.value = true;
driverStoreActive.value = true;
openDriverStorePage();
}
function runUpdateNotificationChecks() {
@@ -1516,6 +1552,7 @@ onUnmounted(() => {
:show-history="showHistory"
:show-sql-library="showSqlLibraryPanel"
:show-driver-store="showDriverStore"
:show-settings-page="showSettingsPage"
:checking-updates="checkingUpdates"
:has-update-available="toolbarHasUpdateAvailable"
:agent-driver-update-count="toolbarAgentDriverUpdateCount"
@@ -1529,10 +1566,7 @@ onUnmounted(() => {
@toggle-sql-library="toggleSqlLibrary"
@open-github="openGitHub"
@open-settings="openSettings()"
@open-driver-store="
driverStoreTabOpen = true;
driverStoreActive = true;
"
@open-driver-store="openDriverStorePage"
@check-updates="checkUpdates()"
@open-transfer="dialogs.showTransferDialog.value = true"
@open-sql-file="dialogs.showSqlFileDialog.value = true"
@@ -1553,20 +1587,32 @@ onUnmounted(() => {
<AppTabBar
:driver-store-open="driverStoreTabOpen"
:driver-store-active="driverStoreActive"
:settings-page-open="settingsPageTabOpen"
:settings-page-active="settingsPageActive"
:agent-driver-update-count="toolbarAgentDriverUpdateCount"
@activate-driver-store="
driverStoreTabOpen = true;
driverStoreActive = true;
"
@activate-tab="driverStoreActive = false"
@close-driver-store="
driverStoreTabOpen = false;
@activate-driver-store="openDriverStorePage"
@activate-settings-page="activateSettingsPage"
@activate-tab="
driverStoreActive = false;
settingsPageActive = false;
"
@close-driver-store="closeDriverStorePage"
@close-settings-page="closeSettingsPage"
@save-tab="handleSaveTab"
/>
<DriverStorePage v-if="driverStoreTabOpen" v-show="driverStoreActive" class="flex-1 min-h-0" :update-notifications-enabled="updateNotificationsEnabled" @update-count-change="updateAgentDriverUpdateCount" />
<div v-if="activeTab" v-show="!driverStoreActive" class="flex flex-col flex-1 min-h-0">
<EditorSettingsPage
v-if="settingsPageTabOpen"
v-show="settingsPageActive"
variant="page"
:open="settingsPageTabOpen"
:initial-tab="settingsInitialTab"
:initial-section="settingsInitialSection"
:app-version="appVersion"
class="flex-1 min-h-0"
@update:open="(open: boolean) => (open ? activateSettingsPage() : closeSettingsPage())"
/>
<div v-if="activeTab" v-show="!driverStoreActive && !settingsPageActive" class="flex flex-col flex-1 min-h-0">
<EditorToolbar
v-if="activeTab.mode === 'query' && !isPreviewTab(activeTab)"
:active-tab="activeTab"
@@ -1655,7 +1701,7 @@ onUnmounted(() => {
</KeepAlive>
</div>
<WelcomeScreen
v-else-if="!driverStoreActive"
v-else-if="!driverStoreActive && !settingsPageActive"
:connection-stats="connectionStats"
:recent-connections="recentConnections"
:saved-sql-history-items="savedSqlHistoryItems"
@@ -1697,10 +1743,6 @@ onUnmounted(() => {
:show-connection-dialog="showConnectionDialog"
:connection-prefill="connectionDialogPrefill"
:connection-initial-tab="connectionDialogInitialTab"
:show-settings-dialog="showSettingsDialog"
:settings-initial-tab="settingsInitialTab"
:settings-initial-section="settingsInitialSection"
:app-version="appVersion"
:show-danger-dialog="showDangerDialog"
:danger-sql="dangerSql"
:suppress-danger-confirm="suppressDangerConfirm"
@@ -1708,7 +1750,6 @@ onUnmounted(() => {
:sql-parameter-source-sql="sqlParameterSourceSql"
:sql-parameter-names="sqlParameterNames"
@update:show-connection-dialog="setConnectionDialogOpen"
@update:show-settings-dialog="showSettingsDialog = $event"
@update:show-danger-dialog="showDangerDialog = $event"
@update:suppress-danger-confirm="suppressDangerConfirm = $event"
@update:show-sql-parameter-dialog="showSqlParameterDialog = $event"
@@ -1727,8 +1768,7 @@ onUnmounted(() => {
"
@open-driver-store="
setConnectionDialogOpen(false);
driverStoreTabOpen = true;
driverStoreActive = true;
openDriverStorePage();
"
@open-lineage-target="openLineageTarget"
@open-database-search-target="openDatabaseSearchTarget"
@@ -94,7 +94,8 @@ let cachedSystemFonts: string[] | null = null;
let pendingSystemFonts: Promise<string[]> | null = null;
const props = defineProps<{
open: boolean;
open?: boolean;
variant?: "dialog" | "page";
initialTab?: string;
initialSection?: string;
appVersion?: string;
@@ -104,6 +105,23 @@ const emit = defineEmits<{
"update:open": [value: boolean];
}>();
const isSettingsPage = computed(() => props.variant === "page");
const settingsVisible = computed(() => isSettingsPage.value || props.open === true);
const settingsRootComponent = computed(() => (isSettingsPage.value ? "div" : Dialog));
const settingsRootProps = computed(() => (isSettingsPage.value ? {} : { open: props.open === true }));
const settingsRootClass = computed(() => (isSettingsPage.value ? "h-full min-h-0 overflow-hidden bg-background" : ""));
const settingsContentComponent = computed(() => (isSettingsPage.value ? "div" : DialogContent));
const settingsContentClass = computed(() => (isSettingsPage.value ? "flex h-full min-h-0 flex-col gap-4 overflow-hidden bg-background p-4" : "h-[min(660px,calc(100dvh-80px))] !max-w-[min(920px,calc(100vw-32px))] grid-rows-[auto_minmax(0,1fr)] gap-3 p-4 sm:!max-w-[min(920px,calc(100vw-48px))]"));
const settingsTitleComponent = computed(() => (isSettingsPage.value ? "h2" : DialogTitle));
function onSettingsRootOpenChange(value: boolean) {
if (!isSettingsPage.value) emit("update:open", value);
}
function closeSettings() {
emit("update:open", false);
}
interface TableColumnTemplateOverrideRow {
id: string;
databaseType: DatabaseType;
@@ -459,7 +477,7 @@ async function loadSystemFontOptions() {
// Sync from store when dialog opens
watch(
() => props.open,
() => settingsVisible.value,
(open) => {
if (open) {
editFontFamily.value = settingsStore.editorSettings.fontFamily;
@@ -641,7 +659,7 @@ async function applySettings() {
async function applySettingsAndClose() {
await persistSettings();
emit("update:open", false);
closeSettings();
}
function resetDefaultsForTab(tab: SettingsCategory) {
@@ -1345,7 +1363,7 @@ async function scrollToInitialSettingsSection() {
}
watch(
() => props.open,
() => settingsVisible.value,
async (open) => {
if (open) {
activeSettingsTab.value = props.initialTab || "editor";
@@ -1374,7 +1392,16 @@ watch(
watch(
() => props.initialSection,
() => {
if (props.open) void scrollToInitialSettingsSection();
if (settingsVisible.value) void scrollToInitialSettingsSection();
},
);
watch(
() => props.initialTab,
(tab) => {
if (!settingsVisible.value || !tab) return;
activeSettingsTab.value = tab;
void scrollToInitialSettingsSection();
},
);
@@ -1848,14 +1875,19 @@ watch(
let previewInitialized = false;
function cleanupPreviewEditor() {
if (!previewView.value) return;
previewView.value.destroy();
previewView.value = null;
previewInitialized = false;
fontThemeComp = null;
themeComp = null;
editorViewModule = null;
}
watch(activeSettingsTab, (tab) => {
if (tab !== "editor" && previewView.value) {
previewView.value.destroy();
previewView.value = null;
previewInitialized = false;
fontThemeComp = null;
themeComp = null;
editorViewModule = null;
cleanupPreviewEditor();
}
});
@@ -1882,28 +1914,23 @@ watch(previewRef, async (el) => {
});
watch(
() => props.open,
() => settingsVisible.value,
(open) => {
if (!open && previewView.value) {
previewView.value.destroy();
previewView.value = null;
previewInitialized = false;
fontThemeComp = null;
themeComp = null;
editorViewModule = null;
}
if (!open) cleanupPreviewEditor();
},
);
onUnmounted(cleanupPreviewEditor);
</script>
<template>
<Dialog :open="open" @update:open="(v: boolean) => emit('update:open', v)">
<DialogContent class="h-[min(660px,calc(100dvh-80px))] !max-w-[min(920px,calc(100vw-32px))] grid-rows-[auto_minmax(0,1fr)] gap-3 p-4 sm:!max-w-[min(920px,calc(100vw-48px))]">
<component :is="settingsRootComponent" v-bind="settingsRootProps" :class="settingsRootClass" @update:open="onSettingsRootOpenChange">
<component :is="settingsContentComponent" :class="settingsContentClass">
<DialogHeader>
<DialogTitle class="flex items-center gap-2">
<component :is="settingsTitleComponent" class="flex items-center gap-2 text-base leading-none font-medium cn-font-heading">
<Settings class="h-4 w-4" />
{{ t("settings.title") }}
</DialogTitle>
</component>
</DialogHeader>
<div class="flex min-h-0 flex-1 flex-col gap-3 overflow-hidden sm:flex-row">
@@ -3519,7 +3546,7 @@ watch(
{{ t("settings.resetDefaults") }}
</Button>
<div class="flex-1" />
<Button variant="outline" @click="emit('update:open', false)">
<Button variant="outline" @click="closeSettings">
{{ t("common.close") }}
</Button>
<Button :disabled="!hasChanges() || hasApplyBlocker" @click="applySettings">
@@ -3556,12 +3583,12 @@ watch(
</Button>
</span>
</div>
<Button variant="outline" @click="emit('update:open', false)">{{ t("common.close") }}</Button>
<Button variant="outline" @click="closeSettings">{{ t("common.close") }}</Button>
<Button :disabled="!aiHasChanges() || !!aiCodexValidationError" @click="aiApplySettings">{{ t("settings.apply") }}</Button>
</DialogFooter>
<DialogFooter v-else-if="activeSettingsTab === 'sync'" class="mx-0 mb-0 flex-row flex-wrap items-center justify-end gap-2 rounded-none border-t border-border/60 bg-transparent px-0 pb-0 pt-3 sm:flex-row sm:gap-2 [&>button]:w-auto [&>button]:shrink-0">
<Button variant="outline" @click="emit('update:open', false)">
<Button variant="outline" @click="closeSettings">
{{ t("common.close") }}
</Button>
<p v-if="webdavMessage" class="text-xs self-center truncate max-w-[280px]" :class="webdavError ? 'text-destructive' : 'text-green-500'">
@@ -3585,7 +3612,7 @@ watch(
</DialogFooter>
<DialogFooter v-else-if="activeSettingsTab === 'mcp' && !isWeb" class="mx-0 mb-0 flex-row flex-wrap items-center justify-end gap-2 rounded-none border-t border-border/60 bg-transparent px-0 pb-0 pt-3 sm:flex-row sm:gap-2 [&>button]:w-auto [&>button]:shrink-0">
<Button variant="outline" @click="emit('update:open', false)">
<Button variant="outline" @click="closeSettings">
{{ t("common.close") }}
</Button>
<div class="flex-1" />
@@ -3601,7 +3628,7 @@ watch(
</DialogFooter>
<DialogFooter v-else-if="activeSettingsTab === 'security' && isWeb" class="mx-0 mb-0 flex-row flex-wrap items-center justify-end gap-2 rounded-none border-t border-border/60 bg-transparent px-0 pb-0 pt-3 sm:flex-row sm:gap-2 [&>button]:w-auto [&>button]:shrink-0">
<Button variant="outline" @click="emit('update:open', false)">
<Button variant="outline" @click="closeSettings">
{{ t("common.close") }}
</Button>
<Button :disabled="changingPassword || !oldPassword || !newPassword || !confirmNewPassword" @click="changePassword">
@@ -3614,7 +3641,7 @@ watch(
{{ t("settings.resetAllDefaults") }}
</Button>
<div class="flex-1" />
<Button variant="outline" @click="emit('update:open', false)">
<Button variant="outline" @click="closeSettings">
{{ t("common.close") }}
</Button>
<Button :disabled="!hasChanges() || hasApplyBlocker" @click="applySettings">
@@ -3626,7 +3653,7 @@ watch(
</DialogFooter>
</div>
</div>
</DialogContent>
</component>
<!-- Theme Customizer Dialog -->
<ThemeCustomizerDialog v-model:open="showThemeCustomizer" :themes="editCustomThemes" :active-theme-id="editActiveCustomThemeId" @save="handleThemeSave" />
@@ -3666,5 +3693,5 @@ watch(
</DialogFooter>
</DialogContent>
</Dialog>
</Dialog>
</component>
</template>
@@ -4,7 +4,6 @@ import { useI18n } from "vue-i18n";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
const ConnectionDialog = defineAsyncComponent(() => import("@/components/connection/ConnectionDialog.vue"));
const EditorSettingsDialog = defineAsyncComponent(() => import("@/components/editor/EditorSettingsDialog.vue"));
const DangerConfirmDialog = defineAsyncComponent(() => import("@/components/editor/DangerConfirmDialog.vue"));
const SqlParameterDialog = defineAsyncComponent(() => import("@/components/editor/SqlParameterDialog.vue"));
const DataTransferDialog = defineAsyncComponent(() => import("@/components/transfer/DataTransferDialog.vue"));
@@ -27,10 +26,6 @@ const props = defineProps<{
showConnectionDialog: boolean;
connectionPrefill?: ConnectionDeepLinkDraft | null;
connectionInitialTab?: ConfigTab;
showSettingsDialog: boolean;
settingsInitialTab?: string;
settingsInitialSection?: string;
appVersion?: string;
showDangerDialog: boolean;
dangerSql: string;
suppressDangerConfirm: boolean;
@@ -41,7 +36,6 @@ const props = defineProps<{
const emit = defineEmits<{
"update:showConnectionDialog": [value: boolean];
"update:showSettingsDialog": [value: boolean];
"update:showDangerDialog": [value: boolean];
"update:suppressDangerConfirm": [value: boolean];
"update:showSqlParameterDialog": [value: boolean];
@@ -117,7 +111,6 @@ watch(
@connect-failed="emit('connectFailed', $event)"
@open-driver-store="emit('openDriverStore')"
/>
<EditorSettingsDialog v-if="showSettingsDialog" :open="showSettingsDialog" :initial-tab="settingsInitialTab || 'editor'" :initial-section="settingsInitialSection" :app-version="appVersion" @update:open="emit('update:showSettingsDialog', $event)" />
<DangerConfirmDialog
v-if="showDangerDialog"
:open="showDangerDialog"
@@ -2,7 +2,7 @@
import { computed, ref, watch, nextTick } from "vue";
import type { CSSProperties } from "vue";
import { useI18n } from "vue-i18n";
import { X, Pin, ChevronDown, Table2, Code2, TableProperties, PencilRuler, KeyRound, Pencil, Package, Lock, Copy, AlertTriangle, Network, Minimize2, Maximize2 } from "@lucide/vue";
import { X, Pin, ChevronDown, Table2, Code2, TableProperties, PencilRuler, KeyRound, Pencil, Package, Lock, Copy, AlertTriangle, Network, Minimize2, Maximize2, Settings } from "@lucide/vue";
import CustomContextMenu, { type ContextMenuItem } from "@/components/ui/CustomContextMenu.vue";
import LightDropdown from "@/components/ui/LightDropdown.vue";
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
@@ -21,6 +21,8 @@ import type { QueryTab } from "@/types/database";
const props = defineProps<{
driverStoreOpen?: boolean;
driverStoreActive?: boolean;
settingsPageOpen?: boolean;
settingsPageActive?: boolean;
agentDriverUpdateCount?: number;
}>();
@@ -28,6 +30,8 @@ const emit = defineEmits<{
"activate-tab": [];
"activate-driver-store": [];
"close-driver-store": [];
"activate-settings-page": [];
"close-settings-page": [];
"save-tab": [tabId: string];
}>();
@@ -218,9 +222,25 @@ watch(
},
);
watch(
() => props.settingsPageActive,
(show) => {
if (!show) return;
nextTick(() => {
const container = tabsContainerRef.value;
if (!container) return;
const el = container.querySelector("[data-settings-page-tab]");
if (el) {
el.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" });
}
updateAllScrollButtons();
});
},
);
function tabColorStyle(tab: QueryTab) {
const color = connectionColor(tab.connectionId);
const isActive = tab.id === queryStore.activeTabId && !props.driverStoreActive;
const isActive = tab.id === queryStore.activeTabId && !props.driverStoreActive && !props.settingsPageActive;
const isClassic = isClassicLayout.value;
if (!color) {
if (isClassic) {
@@ -357,7 +377,7 @@ function activateTab(tabId: string) {
</script>
<template>
<div v-if="queryStore.tabs.length > 0 || driverStoreOpen" class="relative flex w-full min-w-0 shrink-0 overflow-hidden border-b" :class="[isClassicLayout ? 'bg-muted' : 'bg-background', hasFixedTabs ? 'flex-col' : '']">
<div v-if="queryStore.tabs.length > 0 || driverStoreOpen || settingsPageOpen" class="relative flex w-full min-w-0 shrink-0 overflow-hidden border-b" :class="[isClassicLayout ? 'bg-muted' : 'bg-background', hasFixedTabs ? 'flex-col' : '']">
<div class="flex w-full min-w-0 shrink-0 overflow-hidden" :class="isClassicLayout ? 'h-9 items-stretch' : 'h-10 items-center px-2'">
<div class="app-tab-strip relative h-full min-w-0 flex-1 overflow-hidden">
<div v-if="showRegularTabScrollbar" class="app-tab-scrollbar" :class="{ 'app-tab-scrollbar--dragging': isScrollbarDragging }" @pointerdown="startScrollbarDrag">
@@ -372,11 +392,15 @@ function activateTab(tabId: string) {
class="group flex items-center gap-1 px-2 text-xs cursor-pointer transition-colors whitespace-nowrap select-none"
:class="
isClassicLayout
? [compactTabTitle ? 'min-w-24' : 'min-w-38', 'h-full border-r border-border/80 font-medium dark:border-border/45', tab.id === queryStore.activeTabId && !driverStoreActive ? 'bg-background text-foreground' : 'text-foreground/70 hover:text-foreground/90']
: [compactTabTitle ? 'min-w-24' : 'min-w-38', 'h-7 rounded-md border', tab.id === queryStore.activeTabId && !driverStoreActive ? 'text-foreground font-medium' : 'border-border/60 text-foreground/70 hover:border-border hover:text-foreground/90']
? [
compactTabTitle ? 'min-w-24' : 'min-w-38',
'h-full border-r border-border/80 font-medium dark:border-border/45',
tab.id === queryStore.activeTabId && !driverStoreActive && !settingsPageActive ? 'bg-background text-foreground' : 'text-foreground/70 hover:text-foreground/90',
]
: [compactTabTitle ? 'min-w-24' : 'min-w-38', 'h-7 rounded-md border', tab.id === queryStore.activeTabId && !driverStoreActive && !settingsPageActive ? 'text-foreground font-medium' : 'border-border/60 text-foreground/70 hover:border-border hover:text-foreground/90']
"
:style="[tabColorStyle(tab), tabDropStyle(tab.id)]"
:data-active-tab="tab.id === queryStore.activeTabId && !driverStoreActive"
:data-active-tab="tab.id === queryStore.activeTabId && !driverStoreActive && !settingsPageActive"
@click="handleTabClick(tab)"
@dblclick.stop="startRenameTab(tab)"
@mousedown.middle.prevent="queryStore.closeTab(tab.id)"
@@ -428,6 +452,28 @@ function activateTab(tabId: string) {
</div>
</CustomContextMenu>
<!-- Settings Page Tab -->
<div
v-if="settingsPageOpen"
data-settings-page-tab
class="group flex min-w-36 items-center gap-1 px-2 text-xs cursor-pointer transition-colors whitespace-nowrap"
:class="
isClassicLayout
? ['h-full border-r border-border/80 dark:border-border/45 font-medium', settingsPageActive ? 'bg-background text-foreground' : 'text-foreground/70 hover:text-foreground/90']
: ['h-7 rounded-md border font-medium', settingsPageActive ? 'border-ring text-foreground' : 'border-border/60 text-foreground/70 hover:border-border hover:text-foreground/90']
"
:style="isClassicLayout && settingsPageActive ? { boxShadow: '0 1px 0 0 var(--color-background)' } : {}"
@click="emit('activate-settings-page')"
>
<span class="shrink-0 text-sky-600 dark:text-sky-400">
<Settings class="h-3.5 w-3.5" />
</span>
<span class="min-w-0 truncate flex-1">{{ t("settings.title") }}</span>
<button class="rounded hover:bg-muted-foreground/20 p-0.5 shrink-0" @click.stop="emit('close-settings-page')">
<X class="h-3 w-3" />
</button>
</div>
<!-- Driver Store Tab -->
<div
v-if="driverStoreOpen"
@@ -492,11 +538,15 @@ function activateTab(tabId: string) {
class="group flex items-center gap-1 px-2 text-xs cursor-pointer transition-colors whitespace-nowrap select-none"
:class="
isClassicLayout
? [compactTabTitle ? 'min-w-24' : 'min-w-38', 'h-full border-r border-border/80 font-medium dark:border-border/45', tab.id === queryStore.activeTabId && !driverStoreActive ? 'bg-background text-foreground' : 'text-foreground/70 hover:text-foreground/90']
: [compactTabTitle ? 'min-w-24' : 'min-w-38', 'h-7 rounded-md border', tab.id === queryStore.activeTabId && !driverStoreActive ? 'text-foreground font-medium' : 'border-border/60 text-foreground/70 hover:border-border hover:text-foreground/90']
? [
compactTabTitle ? 'min-w-24' : 'min-w-38',
'h-full border-r border-border/80 font-medium dark:border-border/45',
tab.id === queryStore.activeTabId && !driverStoreActive && !settingsPageActive ? 'bg-background text-foreground' : 'text-foreground/70 hover:text-foreground/90',
]
: [compactTabTitle ? 'min-w-24' : 'min-w-38', 'h-7 rounded-md border', tab.id === queryStore.activeTabId && !driverStoreActive && !settingsPageActive ? 'text-foreground font-medium' : 'border-border/60 text-foreground/70 hover:border-border hover:text-foreground/90']
"
:style="[tabColorStyle(tab), tabDropStyle(tab.id)]"
:data-active-tab="tab.id === queryStore.activeTabId && !driverStoreActive"
:data-active-tab="tab.id === queryStore.activeTabId && !driverStoreActive && !settingsPageActive"
@click="handleTabClick(tab)"
@dblclick.stop="startRenameTab(tab)"
@mousedown.middle.prevent="queryStore.closeTab(tab.id)"
@@ -29,6 +29,7 @@ const props = defineProps<{
showHistory: boolean;
showSqlLibrary: boolean;
showDriverStore: boolean;
showSettingsPage: boolean;
checkingUpdates: boolean;
hasUpdateAvailable: boolean;
agentDriverUpdateCount: number;
@@ -491,7 +492,7 @@ const toolbarDropdownTriggerClass = `inline-flex h-8 items-center gap-1 rounded-
<Tooltip>
<TooltipTrigger as-child>
<Button variant="ghost" size="icon" class="h-8 w-8 shrink-0" @click="emit('open-settings')">
<Button variant="ghost" size="icon" class="h-8 w-8 shrink-0" :class="{ 'bg-accent': showSettingsPage }" @click="emit('open-settings')">
<Settings class="h-4 w-4" />
</Button>
</TooltipTrigger>