import * as React from 'react'
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
import { useQuery } from '@tanstack/react-query'
import { ChevronsUpDown, Check, Layers } from 'lucide-react'
import { useConfig, buildDaemonRequest } from '@/state/config'
import { ipc } from '@/lib/api/desktop'
import { qk } from '@/lib/queryKeys'
import { cn } from '@/lib/utils'
const ALL_PROJECTS_LABEL = 'All Projects'
export function ProjectSwitcher() {
const cfg = useConfig()
const daemon = React.useMemo(() => buildDaemonRequest(cfg), [cfg])
const configPath = cfg.configPath
const workbenchQuery = useQuery({
queryKey: qk.workbench(configPath),
queryFn: () =>
ipc.loadWorkbench({
config_path: configPath,
daemon,
}),
enabled: configPath.trim().length > 0,
})
const projectIds = React.useMemo(() => {
const snap = workbenchQuery.data?.snapshot
if (!snap) return [] as string[]
const set = new Set<string>()
for (const entry of [...snap.pending_review, ...snap.wakeup_ready]) {
const pid = entry.record.project_id
if (pid && pid.trim()) set.add(pid)
}
return Array.from(set).sort()
}, [workbenchQuery.data])
const current = cfg.currentProjectId
const currentLabel = current ?? ALL_PROJECTS_LABEL
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<button
type="button"
className={cn(
'flex w-full items-center gap-2 rounded-md border border-border/60 bg-background px-2 py-1.5 text-left text-xs',
'transition-colors hover:bg-muted/50 focus:outline-none focus:ring-1 focus:ring-ring',
)}
>
<Layers className="h-3.5 w-3.5 text-muted-foreground" />
<span className="flex-1 truncate font-medium">{currentLabel}</span>
<ChevronsUpDown className="h-3 w-3 text-muted-foreground" />
</button>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
align="start"
sideOffset={4}
className={cn(
'z-50 min-w-[10rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md',
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
)}
>
<ProjectMenuItem
label={ALL_PROJECTS_LABEL}
active={current === null}
onSelect={() => cfg.setCurrentProjectId(null)}
/>
{projectIds.length > 0 && (
<DropdownMenu.Separator className="my-1 h-px bg-border" />
)}
{projectIds.map((pid) => (
<ProjectMenuItem
key={pid}
label={pid}
active={current === pid}
onSelect={() => cfg.setCurrentProjectId(pid)}
/>
))}
{projectIds.length === 0 && (
<div className="px-2 py-1 text-[10px] text-muted-foreground">
暂无候选项目
</div>
)}
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
)
}
function ProjectMenuItem({
label,
active,
onSelect,
}: {
label: string
active: boolean
onSelect: () => void
}) {
return (
<DropdownMenu.Item
onSelect={onSelect}
className={cn(
'flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1 text-xs outline-none',
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground',
)}
>
<Check className={cn('h-3 w-3', active ? 'opacity-100' : 'opacity-0')} />
<span className="flex-1 truncate">{label}</span>
</DropdownMenu.Item>
)
}