feat: release OpsXShell 2.1.0 native Tauri edition

This commit is contained in:
Logan.Li committed 2026-07-30 23:32:51 +08:00
1 parent e9a0d21758
commit a3dba0c6ed
311 files changed
+31748 -14897

No files matched your search

+144
View File
@@ -0,0 +1,144 @@
/**
* AppShell — 应用壳
*
* 窗口拖拽约定(Tauri overlay title bar):
* - 仅使用 data-tauri-drag-region 处理拖拽和双击缩放
* - 不叠加 CSS app-region 或 React 双击处理,避免一次双击切换两次
* - 标签栏空白区也由 Tauri 原生拖拽区处理
*/
import type { ReactNode } from 'react'
import Sidebar from '../components/layout/Sidebar'
import TabBar from '../components/layout/TabBar'
import TabContent from '../components/layout/TabContent'
import StatusBar from '../components/layout/StatusBar'
import type { Session } from '../stores'
import { getDesktopPlatform } from '../platform/runtime'
export interface AppShellProps {
activeTabId: string | null
isMobile: boolean
sidebarCollapsed: boolean
mobileSidebarOpen: boolean
onToggleMobileSidebar: () => void
onCloseMobileSidebar: () => void
onNewSession: () => void
onQuickConnect: () => void
onOpenLauncher: () => void
onEditSession: (session: Session) => void
children?: ReactNode
}
function TitleBar() {
const platform = getDesktopPlatform()
const leftSpacer = platform === 'macos' ? 'w-[78px]' : 'w-4'
const rightSpacer = platform === 'windows' ? 'w-[138px]' : platform === 'macos' ? 'w-[78px]' : 'w-4'
return (
<header
data-tauri-drag-region
className="bg-dark-900/95 flex h-10 flex-shrink-0 select-none items-center border-b border-dark-800"
>
<div data-tauri-drag-region className={`${leftSpacer} h-full flex-shrink-0`} aria-hidden />
<div data-tauri-drag-region className="flex h-full min-w-0 flex-1 items-center justify-center px-2">
<span className="pointer-events-none truncate text-[11px] tracking-wide text-txt-faint">OpsXShell</span>
</div>
<div data-tauri-drag-region className={`${rightSpacer} h-full flex-shrink-0`} aria-hidden />
</header>
)
}
function MobileMenuButton({ onClick }: { onClick: () => void }) {
return (
<button
type="button"
onClick={onClick}
className="titlebar-no-drag flex h-10 w-10 items-center justify-center text-txt-muted transition-colors hover:bg-dark-800 hover:text-content-primary"
aria-label="切换侧边栏"
>
<svg className="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
)
}
export default function AppShell({
activeTabId,
isMobile,
sidebarCollapsed,
mobileSidebarOpen,
onToggleMobileSidebar,
onCloseMobileSidebar,
onNewSession,
onQuickConnect,
onOpenLauncher,
onEditSession,
children,
}: AppShellProps) {
const handleSidebarNewSession = () => {
onNewSession()
if (isMobile) onCloseMobileSidebar()
}
const handleSidebarQuickConnect = () => {
onQuickConnect()
if (isMobile) onCloseMobileSidebar()
}
const handleSidebarEditSession = (session: Session) => {
onEditSession(session)
if (isMobile) onCloseMobileSidebar()
}
return (
<div className="flex h-screen flex-col overflow-hidden bg-dark-900 text-content-primary">
<TitleBar />
<div className="flex min-h-0 flex-1 overflow-hidden">
{isMobile && mobileSidebarOpen && (
<div
className="titlebar-no-drag fixed inset-0 z-40 bg-black/50 transition-opacity duration-300"
onClick={onCloseMobileSidebar}
aria-hidden
/>
)}
<aside
className={`titlebar-no-drag h-full flex-shrink-0 transition-all duration-300 ease-in-out ${
isMobile
? `fixed inset-y-0 left-0 top-10 z-50 ${mobileSidebarOpen ? 'translate-x-0' : '-translate-x-full'}`
: sidebarCollapsed
? 'w-0 overflow-hidden'
: ''
} `}
>
<Sidebar
onNewSession={handleSidebarNewSession}
onQuickConnect={handleSidebarQuickConnect}
onEditSession={handleSidebarEditSession}
/>
</aside>
<main className="flex min-w-0 flex-1 flex-col bg-dark-800">
<div className="flex items-stretch">
{isMobile && <MobileMenuButton onClick={onToggleMobileSidebar} />}
<div className="min-w-0 flex-1">
<TabBar onOpenLauncher={onOpenLauncher} />
</div>
</div>
<div className="titlebar-no-drag min-h-0 flex-1 overflow-hidden">
<TabContent activeTabId={activeTabId} onNewSession={onNewSession} onQuickConnect={onQuickConnect} />
</div>
</main>
</div>
<div className="titlebar-no-drag">
<StatusBar />
</div>
<div className="titlebar-no-drag">{children}</div>
</div>
)
}