feat: merge titlebar and tab bar into single header with dynamic workspace alignment
- Merge titlebar and tab bar into a single unified top bar (.titlebar) - Remove redundant floating workspace button that caused overlap on collapsed sidebar - Ensure tab bar left aligns with terminal window when workspace sidebar is expanded - Allow tab bar to sit naturally at leftmost position when workspace sidebar is collapsed - Update test runner suite to cover merged titlebar and alignment behavior
This commit is contained in:
+42
-24
@@ -150,9 +150,9 @@ app.whenReady().then(async () => {
|
||||
`);
|
||||
|
||||
// ==========================================
|
||||
// Suite 2: Workspace Hierarchy & Stage Alignment
|
||||
// Suite 2: Merged Titlebar & Workspace Alignment
|
||||
// ==========================================
|
||||
runner.suite('2. 工作区层级结构与视口对齐验证 (UI Architecture)');
|
||||
runner.suite('2. 顶栏与标签页栏合二为一与工作区对齐验证 (UI Architecture)');
|
||||
|
||||
await runner.test('标题栏与工作区标识层级正确 (Titlebar Header)', async () => {
|
||||
const res = await win.webContents.executeJavaScript(`
|
||||
@@ -172,50 +172,68 @@ app.whenReady().then(async () => {
|
||||
if (!res.wsName) throw new Error('Active workspace tag name is empty');
|
||||
});
|
||||
|
||||
await runner.test('工作区终端舞台包含独立头部标签条与视口 (.workspace-stage)', async () => {
|
||||
await runner.test('顶栏和标签页栏合二为一 (Merged Titlebar and Tab Bar)', async () => {
|
||||
const res = await win.webContents.executeJavaScript(`
|
||||
(() => {
|
||||
const stage = document.querySelector('.workspace-stage');
|
||||
const header = document.querySelector('.workspace-header');
|
||||
const viewport = document.getElementById('main-viewport');
|
||||
const titlebar = document.querySelector('.titlebar');
|
||||
const tabBarContainer = document.getElementById('tab-bar-container');
|
||||
const tabBar = document.getElementById('tab-bar');
|
||||
const newTabBtn = document.getElementById('btn-new-tab');
|
||||
const viewport = document.getElementById('main-viewport');
|
||||
const hasOldStage = !!document.querySelector('.workspace-header');
|
||||
return {
|
||||
hasStage: !!stage,
|
||||
hasHeader: !!header,
|
||||
titlebarHasContainer: titlebar?.contains(tabBarContainer),
|
||||
containerHasTabs: tabBarContainer?.contains(tabBar),
|
||||
containerHasBtn: tabBarContainer?.contains(newTabBtn),
|
||||
hasViewport: !!viewport,
|
||||
tabsInHeader: header?.contains(tabBar),
|
||||
btnInHeader: header?.contains(newTabBtn),
|
||||
hasOldHeader: hasOldStage
|
||||
};
|
||||
})()
|
||||
`);
|
||||
if (!res.hasStage) throw new Error('Missing .workspace-stage container');
|
||||
if (!res.hasHeader) throw new Error('Missing .workspace-header');
|
||||
if (!res.titlebarHasContainer) throw new Error('#tab-bar-container must be inside .titlebar');
|
||||
if (!res.containerHasTabs) throw new Error('#tab-bar must be inside #tab-bar-container');
|
||||
if (!res.containerHasBtn) throw new Error('#btn-new-tab must be inside #tab-bar-container');
|
||||
if (!res.hasViewport) throw new Error('Missing #main-viewport');
|
||||
if (!res.tabsInHeader) throw new Error('#tab-bar must be inside .workspace-header');
|
||||
if (!res.btnInHeader) throw new Error('#btn-new-tab must be inside .workspace-header');
|
||||
if (res.hasOldHeader) throw new Error('.workspace-header should be removed (merged into titlebar)');
|
||||
});
|
||||
|
||||
await runner.test('标签栏与终端窗口边缘完全像素级对齐 (100% Alignment)', async () => {
|
||||
await runner.test('工作区展开时标签页和终端窗口左边像素级对齐', async () => {
|
||||
const res = await win.webContents.executeJavaScript(`
|
||||
(() => {
|
||||
const hRect = document.querySelector('.workspace-header').getBoundingClientRect();
|
||||
const tRect = document.getElementById('tab-bar-container').getBoundingClientRect();
|
||||
const vRect = document.getElementById('main-viewport').getBoundingClientRect();
|
||||
return {
|
||||
headerLeft: hRect.left,
|
||||
tabLeft: tRect.left,
|
||||
viewportLeft: vRect.left,
|
||||
headerWidth: hRect.width,
|
||||
viewportWidth: vRect.width,
|
||||
leftDiff: Math.abs(hRect.left - vRect.left),
|
||||
widthDiff: Math.abs(hRect.width - vRect.width)
|
||||
leftDiff: Math.abs(tRect.left - vRect.left)
|
||||
};
|
||||
})()
|
||||
`);
|
||||
if (res.leftDiff >= 1) {
|
||||
throw new Error(`Left mismatch: header.left=${res.headerLeft}, viewport.left=${res.viewportLeft}`);
|
||||
throw new Error(`Left mismatch: tabLeft=${res.tabLeft}, viewportLeft=${res.viewportLeft}`);
|
||||
}
|
||||
if (res.widthDiff >= 1) {
|
||||
throw new Error(`Width mismatch: header.width=${res.headerWidth}, viewport.width=${res.viewportWidth}`);
|
||||
});
|
||||
|
||||
await runner.test('工作区关闭时标签页位于左侧且不强求与终端左边对齐', async () => {
|
||||
const res = await win.webContents.executeJavaScript(`
|
||||
(() => {
|
||||
// 折叠侧边栏
|
||||
window.terminalApp?.workspaceManager?.setCollapsed(true);
|
||||
const tRect = document.getElementById('tab-bar-container').getBoundingClientRect();
|
||||
const titlebarLeft = document.getElementById('titlebar-left').getBoundingClientRect();
|
||||
const vRect = document.getElementById('main-viewport').getBoundingClientRect();
|
||||
// 恢复展开状态供后续测试
|
||||
window.terminalApp?.workspaceManager?.setCollapsed(false);
|
||||
return {
|
||||
tabLeft: tRect.left,
|
||||
controlsRight: titlebarLeft.right,
|
||||
viewportLeft: vRect.left,
|
||||
distFromControls: Math.abs(tRect.left - titlebarLeft.right)
|
||||
};
|
||||
})()
|
||||
`);
|
||||
if (res.distFromControls >= 2) {
|
||||
throw new Error(`Tab bar not immediately beside titlebar-left controls when collapsed: tabLeft=${res.tabLeft}, controlsRight=${res.controlsRight}`);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user