mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-23 07:08:16 +00:00
feat(settings): move settings into main tab
This commit is contained in:
+67
-27
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user