frontend: FE-029 Finder式工作台外壳,毛玻璃侧栏+透明工具栏+折叠过渡动画+动作图标化
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { PanelBottom, PanelLeft, PanelRight } from "lucide-react"
|
||||
import { FolderCog, PanelBottom, PanelLeft, PanelRight, Unplug } from "lucide-react"
|
||||
|
||||
import { Button } from "~/components/ui/button"
|
||||
import { Separator } from "~/components/ui/separator"
|
||||
@@ -36,7 +36,7 @@ export function TopBar({
|
||||
return (
|
||||
<header
|
||||
className={cn(
|
||||
"app-drag flex h-12 shrink-0 items-center justify-between gap-3 border-b bg-background px-3",
|
||||
"app-drag flex h-12 shrink-0 items-center justify-between gap-3 border-b px-3",
|
||||
macInset && "pl-20",
|
||||
)}
|
||||
>
|
||||
@@ -80,12 +80,22 @@ export function TopBar({
|
||||
<PanelBottom />
|
||||
</PanelToggle>
|
||||
<Separator orientation="vertical" className="mx-1 h-4!" />
|
||||
<Button size="sm" variant="outline" onClick={onManageWorkspace}>
|
||||
工作区
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onClick={onReconnect}>
|
||||
重新连接
|
||||
</Button>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button size="icon-sm" variant="ghost" onClick={onManageWorkspace} aria-label="工作区与映射管理">
|
||||
<FolderCog />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">工作区与映射管理</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button size="icon-sm" variant="ghost" onClick={onReconnect} aria-label="重新连接">
|
||||
<Unplug />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">重新连接</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
|
||||
@@ -73,7 +73,8 @@ export function WorkspaceShell({
|
||||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<div className="flex h-svh flex-col overflow-hidden bg-background">
|
||||
{/* Finder 式布局:根容器不铺底色,vibrancy 下毛玻璃从顶栏与左侧栏透出 */}
|
||||
<div className="flex h-svh flex-col overflow-hidden">
|
||||
<TopBar
|
||||
session={session}
|
||||
panels={panels}
|
||||
@@ -99,7 +100,13 @@ export function WorkspaceShell({
|
||||
)}
|
||||
|
||||
<div className="flex min-h-0 flex-1">
|
||||
{panels.tree && (
|
||||
{/* 左右面板保持挂载,通过宽度过渡实现折叠 / 展开动画 */}
|
||||
<div
|
||||
className={cn(
|
||||
"flex min-h-0 shrink-0 overflow-hidden transition-[width] duration-250 ease-out-quart",
|
||||
panels.tree ? "w-[280px]" : "w-0",
|
||||
)}
|
||||
>
|
||||
<SourceTreePanel
|
||||
collection={session.collection}
|
||||
mappings={session.mappings}
|
||||
@@ -107,7 +114,7 @@ export function WorkspaceShell({
|
||||
onNavigate={onNavigate}
|
||||
onFileAction={actions.handleFileAction}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<FolderItemsPanel
|
||||
session={session}
|
||||
selectedServerPath={selectedServerPath}
|
||||
@@ -117,7 +124,12 @@ export function WorkspaceShell({
|
||||
onFileAction={actions.handleFileAction}
|
||||
onRefresh={refreshItems}
|
||||
/>
|
||||
{panels.changes && (
|
||||
<div
|
||||
className={cn(
|
||||
"flex min-h-0 shrink-0 justify-end overflow-hidden transition-[width] duration-250 ease-out-quart",
|
||||
panels.changes ? "w-[340px]" : "w-0",
|
||||
)}
|
||||
>
|
||||
<ChangesPanel
|
||||
mappings={session.mappings}
|
||||
pendingChanges={pending.pendingChanges}
|
||||
@@ -130,16 +142,22 @@ export function WorkspaceShell({
|
||||
onFileAction={actions.handleFileAction}
|
||||
onRefresh={() => void pending.refresh()}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{panels.console && (
|
||||
{/* 底部操作台通过高度过渡实现滑入滑出 */}
|
||||
<div
|
||||
className={cn(
|
||||
"shrink-0 overflow-hidden transition-[height] duration-250 ease-out-quart",
|
||||
panels.console ? "h-[180px]" : "h-0",
|
||||
)}
|
||||
>
|
||||
<ConsolePanel
|
||||
refreshToken={logsRefreshToken}
|
||||
busy={actions.actionBusy || actions.checkinBusy}
|
||||
busyText={actions.notice?.kind === "info" ? actions.notice.text : null}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<WorkspaceDialogs
|
||||
dialog={actions.dialog}
|
||||
|
||||
@@ -129,7 +129,9 @@ export function SourceTreePanel({
|
||||
return (
|
||||
<aside className="flex h-full w-[280px] shrink-0 flex-col border-r bg-sidebar">
|
||||
<div className="flex h-9 shrink-0 items-center gap-2 border-b px-3">
|
||||
<span className="text-xs font-medium text-muted-foreground">源码目录</span>
|
||||
<span className="text-[11px] font-medium tracking-wider text-muted-foreground uppercase">
|
||||
源码目录
|
||||
</span>
|
||||
<span className="ml-auto max-w-28 truncate text-xs text-muted-foreground">{collection}</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
||||
@@ -66,9 +66,11 @@ export function ChangesPanel({
|
||||
}, [onCheckin, included, comment])
|
||||
|
||||
return (
|
||||
<aside className="flex h-full w-[340px] shrink-0 flex-col border-l bg-sidebar">
|
||||
<aside className="flex h-full w-[340px] shrink-0 flex-col border-l bg-background">
|
||||
<div className="flex h-9 shrink-0 items-center gap-2 border-b px-3">
|
||||
<span className="text-xs font-medium text-muted-foreground">挂起更改</span>
|
||||
<span className="text-[11px] font-medium tracking-wider text-muted-foreground uppercase">
|
||||
挂起更改
|
||||
</span>
|
||||
{pendingChanges.length > 0 && (
|
||||
<span className="ml-auto text-xs text-muted-foreground">{pendingChanges.length} 项</span>
|
||||
)}
|
||||
|
||||
@@ -60,9 +60,11 @@ export function ConsolePanel({
|
||||
)
|
||||
|
||||
return (
|
||||
<footer className="flex h-[180px] shrink-0 flex-col border-t bg-background">
|
||||
<footer className="flex h-full flex-col border-t bg-background">
|
||||
<div className="flex h-8 shrink-0 items-center gap-2 border-b px-3">
|
||||
<span className="text-xs font-medium text-muted-foreground">操作日志</span>
|
||||
<span className="text-[11px] font-medium tracking-wider text-muted-foreground uppercase">
|
||||
操作日志
|
||||
</span>
|
||||
{busy && (
|
||||
<span className="flex items-center gap-1.5 text-xs text-primary">
|
||||
<Loader2 className="size-3 animate-spin" />
|
||||
|
||||
Reference in New Issue
Block a user