From bf4874906c8afe7c509f08b82c32ab7186ab275a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E4=B8=AB=E8=AE=B2=E6=A2=B5?= Date: Sat, 12 Sep 2026 21:46:05 +0800 Subject: [PATCH] feat(desktop): unify tab organization menu and adaptive collapsed rail --- .gitignore | 3 + .../src/components/layout/AppTabBar.vue | 2 +- .../components/layout/EditorGroupTabBar.vue | 144 +++++++++++------- .../components/layout/SqlEditorWorkspace.vue | 2 +- .../layout/__tests__/AppTabBar.portal.spec.ts | 4 +- .../EditorGroupTabBar.groups.spec.ts | 20 +-- .../src/components/layout/appTabBar.css | 110 +++++++++++-- apps/desktop/src/i18n/locales/az.ts | 1 + apps/desktop/src/i18n/locales/en.ts | 1 + apps/desktop/src/i18n/locales/es.ts | 1 + apps/desktop/src/i18n/locales/it.ts | 1 + apps/desktop/src/i18n/locales/ja.ts | 1 + apps/desktop/src/i18n/locales/ko.ts | 1 + apps/desktop/src/i18n/locales/pt-BR.ts | 1 + apps/desktop/src/i18n/locales/tr.ts | 1 + apps/desktop/src/i18n/locales/zh-CN.ts | 1 + apps/desktop/src/i18n/locales/zh-TW.ts | 1 + 17 files changed, 212 insertions(+), 83 deletions(-) diff --git a/.gitignore b/.gitignore index ea8c1f5f91..ba7f012d58 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,9 @@ agents/drivers/etcd-go/etcd-go agents/drivers/etcd2-go/etcd2-go plugins/jdbc/.gradle/ plugins/jdbc/build/ +# Java IDE compilation output (keep the launcher scripts in bin/ tracked). +plugins/jdbc/bin/main/ +plugins/jdbc/bin/test/ plugins/jdbc/dist/ docs/.next/ docs/out/ diff --git a/apps/desktop/src/components/layout/AppTabBar.vue b/apps/desktop/src/components/layout/AppTabBar.vue index c8d88f29f3..e516f16a66 100644 --- a/apps/desktop/src/components/layout/AppTabBar.vue +++ b/apps/desktop/src/components/layout/AppTabBar.vue @@ -55,7 +55,7 @@ const layoutClass = computed(() => { }); const navigationStyle = computed(() => { if (!isVerticalLayout.value) return { maxHeight: "50%" }; - const width = props.tabBarCollapsed ? "3.5rem" : `${props.tabBarWidth ?? 240}px`; + const width = props.tabBarCollapsed ? "var(--collapsed-tab-rail-width)" : `${props.tabBarWidth ?? 240}px`; return { width, flex: `0 0 ${width}` }; }); function setTabBarTarget(groupId: string, element: unknown) { diff --git a/apps/desktop/src/components/layout/EditorGroupTabBar.vue b/apps/desktop/src/components/layout/EditorGroupTabBar.vue index d5cebe8503..4fe4347dc0 100644 --- a/apps/desktop/src/components/layout/EditorGroupTabBar.vue +++ b/apps/desktop/src/components/layout/EditorGroupTabBar.vue @@ -30,7 +30,37 @@ const TAB_DRAG_HORIZONTAL_THRESHOLD = 24; import { computed, nextTick, onUnmounted, ref, watch } from "vue"; import type { CSSProperties } from "vue"; import { useI18n } from "vue-i18n"; -import { ArrowDown, ArrowDownUp, ArrowRight, ChevronDown, ChevronsDownUp, ChevronsLeft, ChevronsRight, ChevronsUpDown, Copy, ListFilter, Maximize2, Minimize2, Package, PanelTop, Pencil, Pin, RotateCcw, RotateCw, Search, Settings, X } from "@lucide/vue"; +import { + ArrowDown, + ArrowDownAZ, + ArrowRight, + ChevronDown, + ChevronsDownUp, + ChevronsLeft, + ChevronsRight, + ChevronsUpDown, + Clock3, + Copy, + Database, + ListFilter, + ListOrdered, + Maximize2, + Minimize2, + Package, + PanelBottom, + PanelLeft, + PanelRight, + PanelTop, + Pencil, + Pin, + RotateCcw, + RotateCw, + Search, + Server, + Settings, + Ungroup, + X, +} 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"; @@ -134,7 +164,9 @@ const isWrapLayout = computed(() => !isVerticalLayout.value && settingsStore.edi const isTabBarCollapsed = computed(() => isVerticalLayout.value && !!props.tabBarCollapsed); const tabBarStyle = computed(() => { if (!isVerticalLayout.value) return undefined; - if (props.tabBarCollapsed) return { width: "3.5rem", flex: "0 0 3.5rem" }; + if (props.tabBarCollapsed) { + return { width: "100%", flex: "0 0 100%" }; + } const width = props.tabBarWidth ?? 240; return { width: `${width}px`, flex: `0 0 ${width}px` }; }); @@ -725,36 +757,19 @@ function closeTabGroup(tab: QueryTab) { queryStore.closeTabsByIds(tabsToClose, finalActiveTabId); } -function getTabPreferenceMenuItems(): ContextMenuItem[] { - return [ - { - label: t("settings.tabPlacement"), - icon: PanelTop, - children: tabPlacementItems.value.map((item) => ({ - label: item.label, - checked: item.value === settingsStore.editorSettings.tabPlacement, - action: () => updateTabPlacement(item.value), - })), - }, - { - label: t("settings.tabGroup"), - icon: ListFilter, - children: tabGroupItems.value.map((item) => ({ - label: item.label, - checked: item.value === settingsStore.editorSettings.tabGroupMode, - action: () => updateTabGroupMode(item.value), - })), - }, - { - label: t("settings.tabSort"), - icon: ArrowDownUp, - children: tabSortItems.value.map((item) => ({ - label: item.label, - checked: item.value === settingsStore.editorSettings.tabSortMode, - action: () => updateTabSortMode(item.value), - })), - }, - ]; +const tabOrganizationItems = computed(() => [ + ...tabPlacementItems.value.map((item, index) => ({ ...item, value: `placement:${item.value}`, icon: { top: PanelTop, bottom: PanelBottom, left: PanelLeft, right: PanelRight }[item.value], groupLabel: index === 0 ? t("settings.tabPlacement") : undefined })), + ...tabGroupItems.value.map((item, index) => ({ ...item, value: `group:${item.value}`, icon: { none: Ungroup, "database-type": Database, database: Database, connection: Server }[item.value], separatorBefore: index === 0, groupLabel: index === 0 ? t("settings.tabGroup") : undefined })), + ...tabSortItems.value.map((item, index) => ({ ...item, value: `sort:${item.value}`, icon: { manual: ListOrdered, "created-asc": Clock3, "title-asc": ArrowDownAZ }[item.value], separatorBefore: index === 0, groupLabel: index === 0 ? t("settings.tabSort") : undefined })), +]); +const selectedTabOrganizationItems = computed(() => [`placement:${settingsStore.editorSettings.tabPlacement}`, `group:${settingsStore.editorSettings.tabGroupMode}`, `sort:${settingsStore.editorSettings.tabSortMode}`]); + +function selectTabOrganizationItem(value: string) { + const [section, option] = value.split(":"); + if (!option) return; + if (section === "placement") updateTabPlacement(option); + else if (section === "group") updateTabGroupMode(option); + else if (section === "sort") updateTabSortMode(option); } function getTabGroupMenuItems(tab: QueryTab): ContextMenuItem[] { @@ -785,8 +800,6 @@ function getTabGroupMenuItems(tab: QueryTab): ContextMenuItem[] { visible: settingsStore.editorSettings.tabGroupMode !== "none", }, { label: "", separator: true }, - ...getTabPreferenceMenuItems(), - { label: "", separator: true }, { label: t("contextMenu.closeTabGroup"), action: () => closeTabGroup(tab), @@ -1085,8 +1098,6 @@ function getTabMenuItems(tab: QueryTab): ContextMenuItem[] { onLocate: () => emit("locate-tab", tab), }), { label: "", separator: true }, - ...getTabPreferenceMenuItems(), - { label: "", separator: true }, createPinTabMenuItem({ label: tab.pinned ? t("contextMenu.unpinTab") : t("contextMenu.pinTab"), onToggle: () => queryStore.togglePinnedTab(tab.id), @@ -1402,33 +1413,58 @@ watch([() => props.specialPageTabs?.settingsActive, () => props.specialPageTabs? :data-group-mode="settingsStore.editorSettings.tabGroupMode" :data-placement="settingsStore.editorSettings.tabPlacement" > - +
-
- -
+
+
@@ -1482,7 +1518,7 @@ watch([() => props.specialPageTabs?.settingsActive, () => props.specialPageTabs? @contextmenu="openTabGroupContextMenu($event, onContextMenu)" > -