feat(M8): FR-T4/T5/T6/W2/W6/W7 标签页/布局接线——固定标签悬停浮现关闭按钮(T4); tab-store updateScroll+restore恢复滚动位置(T5); tabbarVisible接线App+TitleBar切换按钮(T6/W6); winIsMaximized IPC+最大化/还原图标切换(W2); 极简模式隐藏TabBar(W7); layout-tabs E2E 3 项

This commit is contained in:
hunter-dev
2026-08-26 15:20:30 +08:00
parent 8f6fd7082b
commit 1f3ede9956
7 changed files with 128 additions and 19 deletions
@@ -1,10 +1,11 @@
import { useTabs } from './tab-store';
import { useStore } from '../../store';
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
export function TabBar() {
const { tabs, activeId, setActive, closeTab, togglePin } = useTabs();
const { openDoc, currentPath } = useStore();
const [hoveredTab, setHoveredTab] = useState<string | null>(null);
// 当 activeId 变化,加载对应标签的文档
useEffect(() => {
@@ -17,19 +18,21 @@ export function TabBar() {
<div data-testid="tabbar" style={{ display: 'flex', borderBottom: '1px solid var(--control-border)', background: 'var(--bg-secondary)', overflowX: 'auto' }}>
{tabs.map(t => (
<div key={t.id} onClick={() => setActive(t.id)}
onMouseEnter={() => setHoveredTab(t.id)} onMouseLeave={() => setHoveredTab(null)}
style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '4px 10px', cursor: 'pointer',
borderBottom: activeId === t.id ? '2px solid var(--accent)' : '2px solid transparent',
background: activeId === t.id ? 'var(--bg-secondary)' : 'transparent', fontSize: 12, color: activeId === t.id ? 'var(--text-primary)' : 'var(--text-tertiary)', whiteSpace: 'nowrap' }}>
{t.pinned && <span style={{ color: 'var(--accent)' }}>★</span>}
<span>{t.path.split('/').pop()?.replace(/\.md$/, '')}</span>
{t.pinned ? (
<span style={{ color: 'var(--text-tertiary)', fontSize: 10 }}>📌</span>
// FR-T4:固定标签悬停时浮现关闭按钮
<span onClick={e => { e.stopPropagation(); closeTab(t.id); }} style={{ color: 'var(--text-tertiary)', cursor: 'pointer', fontSize: 12, opacity: hoveredTab === t.id ? 1 : 0 }} onMouseEnter={e => (e.currentTarget.style.color = '#f88')} onMouseLeave={e => (e.currentTarget.style.color = 'var(--text-tertiary)')}>x</span>
) : (
<span onClick={e => { e.stopPropagation(); closeTab(t.id); }} style={{ color: 'var(--text-tertiary)', cursor: 'pointer', fontSize: 12 }} onMouseEnter={e => (e.currentTarget.style.color = '#f88')} onMouseLeave={e => (e.currentTarget.style.color = 'var(--text-tertiary)')}>×</span>
<span onClick={e => { e.stopPropagation(); closeTab(t.id); }} style={{ color: 'var(--text-tertiary)', cursor: 'pointer', fontSize: 12 }} onMouseEnter={e => (e.currentTarget.style.color = '#f88')} onMouseLeave={e => (e.currentTarget.style.color = 'var(--text-tertiary)')}>x</span>
)}
<span onClick={e => { e.stopPropagation(); togglePin(t.id); }} style={{ color: t.pinned ? 'var(--accent)' : 'var(--text-tertiary)', cursor: 'pointer', fontSize: 10 }} title={t.pinned ? '取消固定' : '固定'}>●</span>
</div>
))}
</div>
);
}
}