refactor(tooltip): use native title for folder picker and sidebar more-options

This commit is contained in:
xintaofei
2026-04-23 11:25:16 +08:00
parent d7113f6ed0
commit cd15813f34
2 changed files with 111 additions and 137 deletions

View File

@@ -23,12 +23,6 @@ import {
CommandItem,
CommandList,
} from "@/components/ui/command"
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip"
import { cn } from "@/lib/utils"
interface ConversationContextBarProps {
@@ -65,53 +59,51 @@ export const ConversationContextBar = memo(function ConversationContextBar({
branches.get(ownFolder.id) ?? ownFolder.git_branch ?? null
return (
<TooltipProvider>
<div className="flex shrink-0 items-center gap-1.5 px-2 pt-2 text-xs text-muted-foreground">
<FolderPicker
folders={folders}
currentFolderId={ownFolder.id}
currentFolderName={ownFolder.name}
editable={isNewConversation}
onSelect={async (folderId) => {
const target = folders.find((f) => f.id === folderId)
if (!target) return
try {
setTabFolder(ownTab.id, target.id, target.path)
toast.success(t("toasts.folderChanged", { name: target.name }))
} catch (err) {
console.error(
"[ConversationContextBar] switch folder failed:",
err
)
toast.error(t("toasts.openFolderFailed"))
}
}}
labelEmpty={t("noFolders")}
labelSearch={t("searchFolder")}
/>
<div className="flex shrink-0 items-center gap-1.5 px-2 pt-2 text-xs text-muted-foreground">
<FolderPicker
folders={folders}
currentFolderId={ownFolder.id}
currentFolderName={ownFolder.name}
editable={isNewConversation}
onSelect={async (folderId) => {
const target = folders.find((f) => f.id === folderId)
if (!target) return
try {
setTabFolder(ownTab.id, target.id, target.path)
toast.success(t("toasts.folderChanged", { name: target.name }))
} catch (err) {
console.error(
"[ConversationContextBar] switch folder failed:",
err
)
toast.error(t("toasts.openFolderFailed"))
}
}}
labelEmpty={t("noFolders")}
labelSearch={t("searchFolder")}
/>
<BranchPicker
folderId={ownFolder.id}
folderPath={ownFolder.path}
currentBranch={currentBranch}
onCheckout={async (branchName) => {
const taskId = `checkout-${ownFolder.id}-${Date.now()}`
addTask(taskId, tBd("tasks.checkoutTo", { branchName }))
updateTask(taskId, { status: "running" })
try {
await gitCheckout(ownFolder.path, branchName)
setBranch(ownFolder.id, branchName)
await refreshFolder(ownFolder.id)
updateTask(taskId, { status: "completed" })
} catch (err) {
const msg = err instanceof Error ? err.message : String(err)
updateTask(taskId, { status: "failed", error: msg })
toast.error(msg)
}
}}
/>
</div>
</TooltipProvider>
<BranchPicker
folderId={ownFolder.id}
folderPath={ownFolder.path}
currentBranch={currentBranch}
onCheckout={async (branchName) => {
const taskId = `checkout-${ownFolder.id}-${Date.now()}`
addTask(taskId, tBd("tasks.checkoutTo", { branchName }))
updateTask(taskId, { status: "running" })
try {
await gitCheckout(ownFolder.path, branchName)
setBranch(ownFolder.id, branchName)
await refreshFolder(ownFolder.id)
updateTask(taskId, { status: "completed" })
} catch (err) {
const msg = err instanceof Error ? err.message : String(err)
updateTask(taskId, { status: "failed", error: msg })
toast.error(msg)
}
}}
/>
</div>
)
})
@@ -146,6 +138,7 @@ const FolderPicker = memo(function FolderPicker({
<Button
variant="outline"
size="xs"
title={currentFolderName}
className={cn(
"min-w-0 bg-transparent",
!editable && "cursor-default opacity-60 hover:bg-transparent"
@@ -158,12 +151,7 @@ const FolderPicker = memo(function FolderPicker({
)
if (!editable) {
return (
<Tooltip>
<TooltipTrigger asChild>{trigger}</TooltipTrigger>
<TooltipContent side="bottom">{currentFolderName}</TooltipContent>
</Tooltip>
)
return trigger
}
return (

View File

@@ -24,12 +24,6 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip"
import { useIsMobile } from "@/hooks/use-mobile"
import {
loadShowCompleted,
@@ -81,80 +75,72 @@ export function Sidebar() {
return (
<aside className="flex h-full min-h-0 flex-col overflow-hidden bg-sidebar text-sidebar-foreground select-none">
<TooltipProvider>
<div className="flex h-10 shrink-0 items-center justify-between gap-2 border-b border-border pl-4 pr-2">
<div className="flex min-w-0 items-baseline gap-[0.375rem]">
<h2 className="truncate text-[0.875rem] font-bold tracking-[-0.00625rem] text-sidebar-foreground">
{t("title")}
</h2>
</div>
<div className="flex items-center gap-0.5">
<Button
variant="ghost"
size="icon"
className="h-6 w-6 shrink-0 text-muted-foreground"
onClick={() => listRef.current?.scrollToActive()}
title={t("locateActiveConversation")}
>
<Crosshair className="h-3.5 w-3.5" />
</Button>
<Button
variant="ghost"
size="icon"
className="h-6 w-6 shrink-0 text-muted-foreground"
onClick={handleToggleExpandAll}
title={
allExpanded ? t("collapseAllGroups") : t("expandAllGroups")
}
>
{allExpanded ? (
<ChevronsDownUp className="h-3.5 w-3.5" />
) : (
<ChevronsUpDown className="h-3.5 w-3.5" />
)}
</Button>
<DropdownMenu>
<Tooltip>
<TooltipTrigger asChild>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="h-6 w-6 shrink-0 text-muted-foreground"
>
<EllipsisVertical className="h-3.5 w-3.5" />
</Button>
</DropdownMenuTrigger>
</TooltipTrigger>
<TooltipContent side="bottom">
{t("moreOptions")}
</TooltipContent>
</Tooltip>
<DropdownMenuContent align="end">
<DropdownMenuCheckboxItem
checked={showCompleted}
onCheckedChange={handleSetShowCompleted}
>
{t("showCompleted")}
</DropdownMenuCheckboxItem>
<DropdownMenuSeparator />
<DropdownMenuLabel>{t("sortBy")}</DropdownMenuLabel>
<DropdownMenuRadioGroup
value={sortMode}
onValueChange={handleSetSortMode}
>
<DropdownMenuRadioItem value="created">
{t("sortByCreatedAt")}
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="updated">
{t("sortByUpdatedAt")}
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="flex h-10 shrink-0 items-center justify-between gap-2 border-b border-border pl-4 pr-2">
<div className="flex min-w-0 items-baseline gap-[0.375rem]">
<h2 className="truncate text-[0.875rem] font-bold tracking-[-0.00625rem] text-sidebar-foreground">
{t("title")}
</h2>
</div>
</TooltipProvider>
<div className="flex items-center gap-0.5">
<Button
variant="ghost"
size="icon"
className="h-6 w-6 shrink-0 text-muted-foreground"
onClick={() => listRef.current?.scrollToActive()}
title={t("locateActiveConversation")}
>
<Crosshair className="h-3.5 w-3.5" />
</Button>
<Button
variant="ghost"
size="icon"
className="h-6 w-6 shrink-0 text-muted-foreground"
onClick={handleToggleExpandAll}
title={
allExpanded ? t("collapseAllGroups") : t("expandAllGroups")
}
>
{allExpanded ? (
<ChevronsDownUp className="h-3.5 w-3.5" />
) : (
<ChevronsUpDown className="h-3.5 w-3.5" />
)}
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="h-6 w-6 shrink-0 text-muted-foreground"
title={t("moreOptions")}
>
<EllipsisVertical className="h-3.5 w-3.5" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuCheckboxItem
checked={showCompleted}
onCheckedChange={handleSetShowCompleted}
>
{t("showCompleted")}
</DropdownMenuCheckboxItem>
<DropdownMenuSeparator />
<DropdownMenuLabel>{t("sortBy")}</DropdownMenuLabel>
<DropdownMenuRadioGroup
value={sortMode}
onValueChange={handleSetSortMode}
>
<DropdownMenuRadioItem value="created">
{t("sortByCreatedAt")}
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="updated">
{t("sortByUpdatedAt")}
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
{/* On mobile, clicking a conversation card auto-closes the Sheet */}
<div