diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index 603912c5b..bfc235473 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -271,6 +271,7 @@ const editConfirmDangerousSqlExecution = ref(settingsStore.editorSettings.confir const editContinueOnErrorOnBatch = ref(settingsStore.editorSettings.continueOnErrorOnBatch); const editConfirmUnsavedSqlClose = ref(settingsStore.editorSettings.confirmUnsavedSqlClose); const editAppLayout = ref(settingsStore.editorSettings.appLayout); +const editTabLayout = ref(settingsStore.editorSettings.tabLayout); const editShowTrayIcon = ref(settingsStore.desktopSettings.show_tray_icon); const editQuitOnClose = ref(settingsStore.desktopSettings.quit_on_close); const desktopCloseBehaviorResetPending = ref(false); @@ -409,6 +410,7 @@ function currentEditorSettingsDraft(): EditorSettingsDraft { continueOnErrorOnBatch: editContinueOnErrorOnBatch.value, confirmUnsavedSqlClose: editConfirmUnsavedSqlClose.value, appLayout: editAppLayout.value, + tabLayout: editTabLayout.value, showColumnCommentsInHeader: editShowColumnCommentsInHeader.value, showColumnTypesInHeader: editShowColumnTypesInHeader.value, compactColumnHeaderActions: editCompactColumnHeaderActions.value, @@ -688,6 +690,7 @@ function syncEditorSettingsDraftFromStore() { editContinueOnErrorOnBatch.value = settingsStore.editorSettings.continueOnErrorOnBatch; editConfirmUnsavedSqlClose.value = settingsStore.editorSettings.confirmUnsavedSqlClose; editAppLayout.value = settingsStore.editorSettings.appLayout; + editTabLayout.value = settingsStore.editorSettings.tabLayout; editShowColumnCommentsInHeader.value = settingsStore.editorSettings.showColumnCommentsInHeader; editShowColumnTypesInHeader.value = settingsStore.editorSettings.showColumnTypesInHeader; editCompactColumnHeaderActions.value = settingsStore.editorSettings.compactColumnHeaderActions; @@ -889,6 +892,7 @@ function resetDefaultsForTab(tab: SettingsCategory) { editCustomThemes.value = [...DEFAULT_EDITOR_SETTINGS.customThemes]; editActiveCustomThemeId.value = DEFAULT_EDITOR_SETTINGS.activeCustomThemeId; editAppLayout.value = DEFAULT_EDITOR_SETTINGS.appLayout; + editTabLayout.value = DEFAULT_EDITOR_SETTINGS.tabLayout; editShowTrayIcon.value = DEFAULT_DESKTOP_SETTINGS.show_tray_icon; editQuitOnClose.value = DEFAULT_DESKTOP_SETTINGS.quit_on_close; desktopCloseBehaviorResetPending.value = true; @@ -1260,6 +1264,10 @@ function setAppLayout(value: InterfaceLayout) { editAppLayout.value = value; } +function setTabLayout(value: "scroll" | "wrap") { + editTabLayout.value = value; +} + function setSidebarActivation(value: "single" | "double") { editSidebarActivation.value = value; } @@ -3234,6 +3242,26 @@ onUnmounted(cleanupPreviewEditor); + + +
+ +
+ + +
+
+
diff --git a/apps/desktop/src/components/layout/AppTabBar.vue b/apps/desktop/src/components/layout/AppTabBar.vue index 4eab8c9ce..f29bd6ccb 100644 --- a/apps/desktop/src/components/layout/AppTabBar.vue +++ b/apps/desktop/src/components/layout/AppTabBar.vue @@ -52,6 +52,7 @@ const tabDrag = useTabDrag((draggedId, targetId, position) => { const editingTabId = ref(null); const editingTitle = ref(""); const isClassicLayout = computed(() => settingsStore.editorSettings.appLayout === "classic"); +const isWrapLayout = computed(() => settingsStore.editorSettings.tabLayout === "wrap"); const fixedTabs = computed(() => queryStore.tabs.filter((tab) => tab.pinned)); const regularTabs = computed(() => queryStore.tabs.filter((tab) => !tab.pinned)); const hasFixedTabs = computed(() => fixedTabs.value.length > 0); @@ -342,12 +343,14 @@ watch( () => queryStore.activeTabId, () => { nextTick(() => { - for (const container of [tabsContainerRef.value, fixedTabsContainerRef.value]) { - if (!container) continue; - const activeEl = container.querySelector('[data-active-tab="true"]'); - if (activeEl) { - activeEl.scrollIntoView({ behavior: tabScrollBehavior.value, block: "nearest", inline: activeTabScrollInline(container, queryStore.activeTabId) }); - break; + if (!isWrapLayout.value) { + for (const container of [tabsContainerRef.value, fixedTabsContainerRef.value]) { + if (!container) continue; + const activeEl = container.querySelector('[data-active-tab="true"]'); + if (activeEl) { + activeEl.scrollIntoView({ behavior: tabScrollBehavior.value, block: "nearest", inline: activeTabScrollInline(container, queryStore.activeTabId) }); + break; + } } } updateAllScrollButtons(); @@ -361,6 +364,7 @@ watch( (show) => { if (!show) return; nextTick(() => { + if (isWrapLayout.value) return; const container = tabsContainerRef.value; if (!container) return; const el = container.querySelector("[data-driver-store-tab]"); @@ -377,6 +381,7 @@ watch( (show) => { if (!show) return; nextTick(() => { + if (isWrapLayout.value) return; const container = tabsContainerRef.value; if (!container) return; const el = container.querySelector("[data-settings-page-tab]"); @@ -434,9 +439,9 @@ function tabDatabaseIconType(tab: QueryTab) { return connection.driver_profile || connection.db_type; } -const showRegularTabScrollbar = computed(() => hasTabOverflow.value); -const showFixedTabScrollbar = computed(() => hasFixedTabOverflow.value); -const showRegularTabOverflowControls = computed(() => regularTabs.value.length > 0 && hasTabOverflow.value); +const showRegularTabScrollbar = computed(() => hasTabOverflow.value && !isWrapLayout.value); +const showFixedTabScrollbar = computed(() => hasFixedTabOverflow.value && !isWrapLayout.value); +const showRegularTabOverflowControls = computed(() => regularTabs.value.length > 0 && hasTabOverflow.value && !isWrapLayout.value); const regularTabOverflowOpen = ref(false); const fixedTabOverflowOpen = ref(false); const tabBarClass = computed(() => [isClassicLayout.value ? "bg-muted" : "border-b bg-background", hasFixedTabs.value ? "flex-col" : "", isClassicLayout.value && hasFixedTabs.value ? "border-b" : ""]); @@ -504,8 +509,8 @@ const fixedTabScrollbarThumbStyle = computed(() => ({ width: `${fixedScrollThumbWidthPercent.value}%`, })); -const tabTailDragRegionClass = computed(() => (showRegularTabOverflowControls.value ? "w-0 flex-none self-stretch" : "min-w-8 flex-1 self-stretch")); -const fixedTabTailDragRegionClass = computed(() => (showFixedTabScrollbar.value ? "w-0 flex-none self-stretch" : "min-w-8 flex-1 self-stretch")); +const tabTailDragRegionClass = computed(() => (showRegularTabOverflowControls.value || isWrapLayout.value ? "w-0 flex-none self-stretch" : "min-w-8 flex-1 self-stretch")); +const fixedTabTailDragRegionClass = computed(() => (showFixedTabScrollbar.value || isWrapLayout.value ? "w-0 flex-none self-stretch" : "min-w-8 flex-1 self-stretch")); const tabOverflowControlClass = computed(() => isClassicLayout.value @@ -551,7 +556,14 @@ function onOverflowItemKeydown(event: KeyboardEvent, tabId: string, kind: "regul
-
+
@@ -735,7 +747,14 @@ function onOverflowItemKeydown(event: KeyboardEvent, tabId: string, kind: "regul
-
+
@@ -916,6 +935,45 @@ function onOverflowItemKeydown(event: KeyboardEvent, tabId: string, kind: "regul