refactor(conversation-context-bar): slim folder and branch pickers to selection only

- Match popover corner styling of folder picker with branch picker
- Drop 'open folder from disk' entry from folder picker
- Drop 'new branch' entry and dialog from branch picker
This commit is contained in:
xintaofei
2026-04-22 19:30:58 +08:00
parent d26c0b91c9
commit 4440249678
11 changed files with 77 additions and 236 deletions

View File

@@ -3,23 +3,13 @@
import { memo, useCallback, useEffect, useMemo, useState } from "react"
import { useTranslations } from "next-intl"
import { toast } from "sonner"
import {
Check,
ChevronsUpDown,
Folder,
FolderOpen,
GitBranch,
Loader2,
Plus,
} from "lucide-react"
import { Check, ChevronsUpDown, Folder, GitBranch, Loader2 } from "lucide-react"
import { useAppWorkspace } from "@/contexts/app-workspace-context"
import { useTabContext } from "@/contexts/tab-context"
import { useTaskContext } from "@/contexts/task-context"
import { gitListAllBranches, gitCheckout, gitNewBranch } from "@/lib/api"
import { isDesktop, openFileDialog } from "@/lib/platform"
import { gitListAllBranches, gitCheckout } from "@/lib/api"
import type { GitBranchList } from "@/lib/types"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import {
Popover,
PopoverContent,
@@ -32,15 +22,7 @@ import {
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
} from "@/components/ui/command"
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
import {
Tooltip,
TooltipContent,
@@ -64,7 +46,6 @@ export const ConversationContextBar = memo(function ConversationContextBar({
allFolders,
branches,
setBranch,
openFolder,
addFolderToWorkspaceById,
refreshFolder,
} = useAppWorkspace()
@@ -115,25 +96,6 @@ export const ConversationContextBar = memo(function ConversationContextBar({
toast.error(t("toasts.openFolderFailed"))
}
}}
onOpenNewFolder={async () => {
try {
if (isDesktop()) {
const result = await openFileDialog({
directory: true,
multiple: false,
})
if (!result) return
const selected = Array.isArray(result) ? result[0] : result
const detail = await openFolder(selected)
setTabFolder(ownTab.id, detail.id, detail.path)
toast.success(t("toasts.folderChanged", { name: detail.name }))
}
} catch (err) {
console.error("[ConversationContextBar] open folder failed:", err)
toast.error(t("toasts.openFolderFailed"))
}
}}
labelOpenNew={t("openNewFolder")}
labelEmpty={t("noFolders")}
labelSearch={t("searchFolder")}
/>
@@ -157,21 +119,6 @@ export const ConversationContextBar = memo(function ConversationContextBar({
toast.error(msg)
}
}}
onNewBranch={async (branchName, startPoint) => {
const taskId = `new-branch-${ownFolder.id}-${Date.now()}`
addTask(taskId, tBd("tasks.newBranch", { name: branchName }))
updateTask(taskId, { status: "running" })
try {
await gitNewBranch(ownFolder.path, branchName, startPoint)
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>
@@ -190,8 +137,6 @@ interface FolderPickerProps {
currentFolderName: string
editable: boolean
onSelect: (folderId: number) => void | Promise<void>
onOpenNewFolder: () => void | Promise<void>
labelOpenNew: string
labelEmpty: string
labelSearch: string
}
@@ -202,8 +147,6 @@ const FolderPicker = memo(function FolderPicker({
currentFolderName,
editable,
onSelect,
onOpenNewFolder,
labelOpenNew,
labelEmpty,
labelSearch,
}: FolderPickerProps) {
@@ -236,8 +179,8 @@ const FolderPicker = memo(function FolderPicker({
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>{trigger}</PopoverTrigger>
<PopoverContent align="start" className="p-0 w-72">
<Command>
<PopoverContent align="start" className="p-0 w-72 overflow-hidden">
<Command className="rounded-2xl">
<CommandInput placeholder={labelSearch} />
<CommandList>
<CommandEmpty>{labelEmpty}</CommandEmpty>
@@ -264,18 +207,6 @@ const FolderPicker = memo(function FolderPicker({
</CommandItem>
))}
</CommandGroup>
<CommandSeparator />
<CommandGroup>
<CommandItem
onSelect={() => {
setOpen(false)
void onOpenNewFolder()
}}
>
<FolderOpen className="h-4 w-4" />
{labelOpenNew}
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
@@ -292,7 +223,6 @@ interface BranchPickerProps {
folderPath: string
currentBranch: string | null
onCheckout: (branchName: string) => Promise<void>
onNewBranch: (branchName: string, startPoint?: string) => Promise<void>
}
const BranchPicker = memo(function BranchPicker({
@@ -300,15 +230,12 @@ const BranchPicker = memo(function BranchPicker({
folderPath,
currentBranch,
onCheckout,
onNewBranch,
}: BranchPickerProps) {
const t = useTranslations("Folder.conversationContextBar")
const tBd = useTranslations("Folder.branchDropdown")
const [open, setOpen] = useState(false)
const [branchList, setBranchList] = useState<GitBranchList | null>(null)
const [loading, setLoading] = useState(false)
const [newBranchOpen, setNewBranchOpen] = useState(false)
const [newBranchName, setNewBranchName] = useState("")
const loadBranches = useCallback(async () => {
setLoading(true)
@@ -333,14 +260,9 @@ const BranchPicker = memo(function BranchPicker({
}, [folderId])
return (
<>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
size="xs"
className="min-w-0 bg-transparent"
>
<Button variant="outline" size="xs" className="min-w-0 bg-transparent">
<GitBranch className="size-3 shrink-0 text-muted-foreground" />
<span className="max-w-[160px] truncate">
{currentBranch ?? t("noBranch")}
@@ -359,21 +281,7 @@ const BranchPicker = memo(function BranchPicker({
) : (
<>
<CommandEmpty>{t("noBranches")}</CommandEmpty>
<CommandGroup>
<CommandItem
onSelect={() => {
setOpen(false)
setNewBranchName("")
setNewBranchOpen(true)
}}
>
<Plus className="h-4 w-4" />
{tBd("newBranch")}
</CommandItem>
</CommandGroup>
{branchList && branchList.local.length > 0 && (
<>
<CommandSeparator />
<CommandGroup
heading={tBd("localBranches", {
count: branchList.local.length,
@@ -396,7 +304,6 @@ const BranchPicker = memo(function BranchPicker({
</CommandItem>
))}
</CommandGroup>
</>
)}
{branchList && branchList.remote.length > 0 && (
<CommandGroup
@@ -427,41 +334,5 @@ const BranchPicker = memo(function BranchPicker({
</Command>
</PopoverContent>
</Popover>
<Dialog open={newBranchOpen} onOpenChange={setNewBranchOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>{tBd("dialogs.newBranchTitle")}</DialogTitle>
</DialogHeader>
<div className="text-sm text-muted-foreground">
{tBd("dialogs.newBranchDescription", {
branch: currentBranch ?? "-",
})}
</div>
<Input
placeholder={tBd("dialogs.branchNamePlaceholder")}
value={newBranchName}
onChange={(e) => setNewBranchName(e.target.value)}
autoFocus
/>
<DialogFooter>
<Button variant="outline" onClick={() => setNewBranchOpen(false)}>
{t("cancel")}
</Button>
<Button
disabled={!newBranchName.trim()}
onClick={async () => {
const name = newBranchName.trim()
if (!name) return
setNewBranchOpen(false)
await onNewBranch(name, currentBranch ?? undefined)
}}
>
{t("create")}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</>
)
})

View File

@@ -1797,9 +1797,6 @@
"noFolders": "No folders",
"noBranches": "No branches",
"noBranch": "(no branch)",
"openNewFolder": "Open folder from disk...",
"cancel": "Cancel",
"create": "Create",
"commit": "Commit",
"push": "Push",
"merge": "Merge",

View File

@@ -1797,9 +1797,6 @@
"noFolders": "No folders",
"noBranches": "No branches",
"noBranch": "(no branch)",
"openNewFolder": "Open folder from disk...",
"cancel": "Cancel",
"create": "Create",
"commit": "Commit",
"push": "Push",
"merge": "Merge",

View File

@@ -1797,9 +1797,6 @@
"noFolders": "No folders",
"noBranches": "No branches",
"noBranch": "(no branch)",
"openNewFolder": "Open folder from disk...",
"cancel": "Cancel",
"create": "Create",
"commit": "Commit",
"push": "Push",
"merge": "Merge",

View File

@@ -1797,9 +1797,6 @@
"noFolders": "No folders",
"noBranches": "No branches",
"noBranch": "(no branch)",
"openNewFolder": "Open folder from disk...",
"cancel": "Cancel",
"create": "Create",
"commit": "Commit",
"push": "Push",
"merge": "Merge",

View File

@@ -1797,9 +1797,6 @@
"noFolders": "No folders",
"noBranches": "No branches",
"noBranch": "(no branch)",
"openNewFolder": "Open folder from disk...",
"cancel": "Cancel",
"create": "Create",
"commit": "Commit",
"push": "Push",
"merge": "Merge",

View File

@@ -1797,9 +1797,6 @@
"noFolders": "No folders",
"noBranches": "No branches",
"noBranch": "(no branch)",
"openNewFolder": "Open folder from disk...",
"cancel": "Cancel",
"create": "Create",
"commit": "Commit",
"push": "Push",
"merge": "Merge",

View File

@@ -1797,9 +1797,6 @@
"noFolders": "No folders",
"noBranches": "No branches",
"noBranch": "(no branch)",
"openNewFolder": "Open folder from disk...",
"cancel": "Cancel",
"create": "Create",
"commit": "Commit",
"push": "Push",
"merge": "Merge",

View File

@@ -1797,9 +1797,6 @@
"noFolders": "No folders",
"noBranches": "No branches",
"noBranch": "(no branch)",
"openNewFolder": "Open folder from disk...",
"cancel": "Cancel",
"create": "Create",
"commit": "Commit",
"push": "Push",
"merge": "Merge",

View File

@@ -1797,9 +1797,6 @@
"noFolders": "暂无文件夹",
"noBranches": "暂无分支",
"noBranch": "(无分支)",
"openNewFolder": "从磁盘打开文件夹...",
"cancel": "取消",
"create": "创建",
"commit": "提交",
"push": "推送",
"merge": "合并",

View File

@@ -1797,9 +1797,6 @@
"noFolders": "No folders",
"noBranches": "No branches",
"noBranch": "(no branch)",
"openNewFolder": "Open folder from disk...",
"cancel": "Cancel",
"create": "Create",
"commit": "Commit",
"push": "Push",
"merge": "Merge",