frontend: FE-029 工作台外壳改版,顶栏徽片化+动作图标化+三栏折叠过渡动画

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
fenghp
2026-06-11 17:22:46 +08:00
co-authored by Cursor
parent ad7c30393c
commit b6447443c5
7 changed files with 77 additions and 34 deletions
+27 -18
View File
@@ -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"
@@ -41,20 +41,19 @@ export function TopBar({
)}
>
<div className="flex min-w-0 items-center gap-2 text-sm">
<span className="shrink-0 font-semibold">MacTFS</span>
<span className="shrink-0 font-semibold tracking-tight">MacTFS</span>
<Separator orientation="vertical" className="h-4!" />
<span className="hidden shrink-0 text-xs text-muted-foreground lg:inline">
服务器
</span>
<span className="hidden max-w-56 truncate font-mono text-xs lg:inline">
<span className="hidden max-w-56 truncate font-mono text-xs text-muted-foreground lg:inline">
{session.serverUri}
</span>
<Separator orientation="vertical" className="hidden h-4! lg:block" />
<span className="shrink-0 text-xs text-muted-foreground">Collection</span>
<span className="max-w-40 truncate text-xs font-medium">{session.collection}</span>
<Separator orientation="vertical" className="h-4!" />
<span className="shrink-0 text-xs text-muted-foreground">Workspace</span>
<span className="max-w-44 truncate font-mono text-xs">{session.workspace}</span>
<span className="hidden max-w-44 shrink-0 items-center gap-1 truncate rounded-md bg-muted px-2 py-0.5 text-xs font-medium sm:inline-flex">
<span className="text-muted-foreground">Collection</span>
<span className="truncate">{session.collection}</span>
</span>
<span className="inline-flex max-w-52 shrink-0 items-center gap-1 truncate rounded-md bg-muted px-2 py-0.5 text-xs">
<span className="text-muted-foreground">Workspace</span>
<span className="truncate font-mono">{session.workspace}</span>
</span>
</div>
<div className="app-no-drag flex shrink-0 items-center gap-1">
@@ -80,12 +79,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>
)
@@ -99,7 +99,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 +113,7 @@ export function WorkspaceShell({
onNavigate={onNavigate}
onFileAction={actions.handleFileAction}
/>
)}
</div>
<FolderItemsPanel
session={session}
selectedServerPath={selectedServerPath}
@@ -117,7 +123,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 +141,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"
@@ -68,7 +68,9 @@ export function ChangesPanel({
return (
<aside className="flex h-full w-[340px] shrink-0 flex-col border-l 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>
{pendingChanges.length > 0 && (
<span className="ml-auto text-xs text-muted-foreground">{pendingChanges.length} 项</span>
)}
@@ -62,7 +62,9 @@ export function ConsolePanel({
return (
<footer className="flex h-[180px] shrink-0 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" />