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:
@@ -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>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1797,9 +1797,6 @@
|
||||
"noFolders": "暂无文件夹",
|
||||
"noBranches": "暂无分支",
|
||||
"noBranch": "(无分支)",
|
||||
"openNewFolder": "从磁盘打开文件夹...",
|
||||
"cancel": "取消",
|
||||
"create": "创建",
|
||||
"commit": "提交",
|
||||
"push": "推送",
|
||||
"merge": "合并",
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user