From 9344e4f3ef5d7be36815f37d67e475eddf30c970 Mon Sep 17 00:00:00 2001
From: Levi <9320732+posinega@user.noreply.gitee.com>
Date: Mon, 23 Mar 2026 16:54:09 +0800
Subject: [PATCH] feat: enhance TerminalLayout with preview actions and
overflow handling
---
.../src/views/layouts/TerminalLayout.vue | 22 +++++++++++++
.../layouts/__tests__/TerminalLayout.test.ts | 32 +++++++++++++++++++
.../layouts/components/EditorActions.vue | 6 ++++
3 files changed, 60 insertions(+)
diff --git a/src/renderer/src/views/layouts/TerminalLayout.vue b/src/renderer/src/views/layouts/TerminalLayout.vue
index 62b37b6a8..2927f2514 100644
--- a/src/renderer/src/views/layouts/TerminalLayout.vue
+++ b/src/renderer/src/views/layouts/TerminalLayout.vue
@@ -204,6 +204,7 @@
>
@@ -218,6 +219,7 @@
v-if="configLoaded"
ref="dockviewRef"
:class="currentTheme === 'light' ? 'dockview-theme-light' : 'dockview-theme-dark'"
+ :disable-tabs-overflow-list="true"
:style="{
width: '100%',
height: '100%',
@@ -2251,6 +2253,14 @@ const panelCount = ref(0)
const hasPanels = computed(() => panelCount.value > 0)
let dockApi: DockviewApi | null = null
const dockApiInstance = ref(null)
+const isPreviewActionsVisible = ref(false)
+
+const computePreviewActionsVisible = (): boolean => {
+ const panel = dockApi?.activePanel
+ if (!panel) return false
+ // Keep in sync with `EditorActions.vue` showActions logic.
+ return panel.params?.content === 'KnowledgeCenterEditor' && panel.params?.mode !== 'preview' && panel.params?.isMarkdown
+}
const isFocusInTerminal = (event: KeyboardEvent): boolean => {
const target = event.target as HTMLElement | null
@@ -2377,6 +2387,7 @@ const handleActivePanelChange = async () => {
const onDockReady = (event: DockviewReadyEvent) => {
dockApi = event.api
dockApiInstance.value = event.api
+ isPreviewActionsVisible.value = computePreviewActionsVisible()
dockApi.onDidAddPanel(() => {
panelCount.value = dockApi?.panels.length ?? 0
@@ -2392,6 +2403,7 @@ const onDockReady = (event: DockviewReadyEvent) => {
})
dockApi.onDidActivePanelChange(() => {
+ isPreviewActionsVisible.value = computePreviewActionsVisible()
handleActivePanelChange()
})
@@ -2958,6 +2970,16 @@ defineExpose({
right: 0;
z-index: 10;
}
+
+ // Reserve space for the preview actions overlay so it never covers
+ // Dockview header tabs when they overflow/clamp.
+ &.has-preview-actions {
+ .dockview-theme-light .dv-tabs-and-actions-container,
+ .dockview-theme-dark .dv-tabs-and-actions-container {
+ padding-right: 30px;
+ box-sizing: border-box;
+ }
+ }
}
.ant-input-group-wrapper {
diff --git a/src/renderer/src/views/layouts/__tests__/TerminalLayout.test.ts b/src/renderer/src/views/layouts/__tests__/TerminalLayout.test.ts
index f3a6c8505..76de03ba7 100644
--- a/src/renderer/src/views/layouts/__tests__/TerminalLayout.test.ts
+++ b/src/renderer/src/views/layouts/__tests__/TerminalLayout.test.ts
@@ -8,6 +8,8 @@
*/
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
+import { readFileSync } from 'node:fs'
+import { join } from 'node:path'
import { ref } from 'vue'
// Mocks (required by AI Sidebar tests)
@@ -439,6 +441,36 @@ describe('TerminalLayout - Close Tab Keyboard Shortcut', () => {
})
})
+describe('TerminalLayout - Dockview Tabs Overflow', () => {
+ it('should disable tabs overflow dropdown list', () => {
+ const sourcePath = join(process.cwd(), 'src/renderer/src/views/layouts/TerminalLayout.vue')
+ const source = readFileSync(sourcePath, 'utf8')
+ expect(source).toContain('disable-tabs-overflow-list')
+ })
+})
+
+describe('TerminalLayout - Preview Actions Layout', () => {
+ it('should reserve right padding for preview actions overlay', () => {
+ const sourcePath = join(process.cwd(), 'src/renderer/src/views/layouts/TerminalLayout.vue')
+ const source = readFileSync(sourcePath, 'utf8')
+ expect(source).toContain('padding-right: 30px')
+ })
+
+ it('should set fixed width for preview actions button container', () => {
+ const sourcePath = join(process.cwd(), 'src/renderer/src/views/layouts/components/EditorActions.vue')
+ const source = readFileSync(sourcePath, 'utf8')
+ expect(source).toContain('width: 30px')
+ expect(source).toContain('min-width: 30px')
+ })
+
+ it('should only apply padding when preview actions are visible', () => {
+ const sourcePath = join(process.cwd(), 'src/renderer/src/views/layouts/TerminalLayout.vue')
+ const source = readFileSync(sourcePath, 'utf8')
+ expect(source).toContain('has-preview-actions')
+ expect(source).toContain('computePreviewActionsVisible')
+ })
+})
+
// ========== Tab Context Menu ==========
describe('TerminalLayout - Tab Context Menu', () => {
let mockDockApi: any
diff --git a/src/renderer/src/views/layouts/components/EditorActions.vue b/src/renderer/src/views/layouts/components/EditorActions.vue
index c2efd0770..9b18e6eb3 100644
--- a/src/renderer/src/views/layouts/components/EditorActions.vue
+++ b/src/renderer/src/views/layouts/components/EditorActions.vue
@@ -90,6 +90,11 @@ const openPreview = () => {
display: flex;
align-items: center;
padding: 0 8px;
+ width: 30px;
+ min-width: 30px;
+ flex: 0 0 30px;
+ flex-shrink: 0;
+ box-sizing: border-box;
height: 35px; /* Match tab height */
background: var(--bg-color-secondary);
border-bottom: 1px solid var(--border-color);
@@ -102,6 +107,7 @@ const openPreview = () => {
justify-content: center;
width: 24px;
height: 24px;
+ flex-shrink: 0;
cursor: pointer;
border-radius: 4px;
color: var(--text-color-secondary);