feat: release OpsXShell 2.1.0 native Tauri edition
This commit is contained in:
1 parent
e9a0d21758
commit
a3dba0c6ed
311 files changed
+31748
-14897
No files matched your search
@@ -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>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user