From 421857f2496554e1252b3576d04c49c630330af5 Mon Sep 17 00:00:00 2001 From: xintaofei Date: Tue, 24 Mar 2026 23:53:37 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96folder=E7=AA=97=E5=8F=A3?= =?UTF-8?q?=E9=A1=B6=E9=83=A8=E7=9A=84=E6=A8=A1=E5=BC=8F=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/folder-title-bar.tsx | 83 +++++++++++++++++++--- 1 file changed, 72 insertions(+), 11 deletions(-) diff --git a/src/components/layout/folder-title-bar.tsx b/src/components/layout/folder-title-bar.tsx index eeb616c..0db292e 100644 --- a/src/components/layout/folder-title-bar.tsx +++ b/src/components/layout/folder-title-bar.tsx @@ -38,6 +38,7 @@ import { FolderNameDropdown } from "./folder-name-dropdown" import { BranchDropdown } from "./branch-dropdown" import { CommandDropdown } from "./command-dropdown" import { SearchCommandDialog } from "@/components/conversations/search-command-dialog" +import { cn } from "@/lib/utils" const MODE_TABS = [ { @@ -185,8 +186,43 @@ export function FolderTitleBar() { setBranch(null) } }, [folderPath]) - const modeIndex = MODE_TABS.findIndex((item) => item.mode === mode) - const indicatorLeft = `${2 + modeIndex * 32}px` + const modeContainerRef = useRef(null) + const modeItemRefs = useRef>(new Map()) + const [modeIndicator, setModeIndicator] = useState<{ + left: number + width: number + } | null>(null) + + useEffect(() => { + const container = modeContainerRef.current + if (!container) return + + const measure = () => { + const btn = modeItemRefs.current.get(mode) + if (!btn || !container) { + setModeIndicator(null) + return + } + const containerRect = container.getBoundingClientRect() + const btnRect = btn.getBoundingClientRect() + setModeIndicator({ + left: btnRect.left - containerRect.left, + width: btnRect.width, + }) + } + + const ro = new ResizeObserver(() => measure()) + for (const btn of modeItemRefs.current.values()) { + ro.observe(btn) + } + ro.observe(container) + measure() + + return () => { + ro.disconnect() + } + }, [mode]) + const handleModeKeyDown = useCallback( (event: ReactKeyboardEvent, nextMode: typeof mode) => { if (event.key === "Enter" || event.key === " ") { @@ -214,14 +250,20 @@ export function FolderTitleBar() { } center={
-
+ {modeIndicator && ( +
+ )} {MODE_TABS.map((item) => { const Icon = item.icon const isActive = mode === item.mode @@ -229,17 +271,26 @@ export function FolderTitleBar() { return (
{ + if (el) { + modeItemRefs.current.set(item.mode, el) + } else { + modeItemRefs.current.delete(item.mode) + } + }} role="tab" tabIndex={0} - className={`relative z-10 m-0 flex h-[23px] w-8 cursor-pointer select-none items-center justify-center rounded-full border-0 bg-transparent p-0 align-middle leading-none transition-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60 ${ + className={cn( + "relative z-10 m-0 flex h-[23px] cursor-pointer select-none items-center justify-center gap-1 rounded-full border-0 bg-transparent p-0 align-middle text-xs font-medium leading-none transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60", + isActive ? "px-2.5" : "px-2", isActive ? "text-foreground" - : "text-muted-foreground hover:text-foreground/80" - }`} + : "text-muted-foreground hover:text-foreground/70" + )} onClick={() => setMode(item.mode)} onKeyDown={(event) => handleModeKeyDown(event, item.mode)} onMouseDown={(event) => event.preventDefault()} - title={title} + title={!isActive ? title : undefined} aria-label={title} aria-selected={isActive} > @@ -247,6 +298,16 @@ export function FolderTitleBar() { className="block h-3 w-3 shrink-0" shapeRendering="geometricPrecision" /> + + {title} +
) })}