import React from 'react'; import { api } from '@renderer/api'; import { ProviderBrandLogo } from '@renderer/components/common/ProviderBrandLogo'; import { Button } from '@renderer/components/ui/button'; import { Combobox } from '@renderer/components/ui/combobox'; import { Input } from '@renderer/components/ui/input'; import { Label } from '@renderer/components/ui/label'; import { cn } from '@renderer/lib/utils'; import { Check, FolderOpen } from 'lucide-react'; import { buildProjectPathOptions, type ProjectPathOptionMeta, type ProjectPathProject, } from './projectPathOptions'; import type { DashboardRecentProjectSource } from '@features/recent-projects/contracts'; import type { ComboboxOption } from '@renderer/components/ui/combobox'; function escapeRegExp(value: string): string { return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } function renderHighlightedText(text: string, query: string): React.JSX.Element { if (!query.trim()) { return {text}; } const pattern = new RegExp(`(${escapeRegExp(query)})`, 'ig'); const parts = text.split(pattern); return ( {parts.map((part, index) => { const isMatch = part.toLowerCase() === query.toLowerCase(); if (!isMatch) { return {part}; } return ( {part} ); })} ); } function getOptionSource(option: ComboboxOption): DashboardRecentProjectSource | undefined { return (option.meta as ProjectPathOptionMeta | undefined)?.discoverySource; } function getSourceLabel(source: DashboardRecentProjectSource): string { switch (source) { case 'claude': return 'Found by Claude'; case 'codex': return 'Found by Codex'; case 'mixed': return 'Found by Claude and Codex'; } } const ProjectSourceBadge = ({ source, }: { source?: DashboardRecentProjectSource; }): React.JSX.Element | null => { if (!source) { return null; } const logos = source === 'mixed' ? (['anthropic', 'codex'] as const) : source === 'codex' ? (['codex'] as const) : (['anthropic'] as const); return ( {logos.map((providerId) => ( ))} ); }; export type CwdMode = 'project' | 'custom'; interface ProjectPathSelectorProps { cwdMode: CwdMode; onCwdModeChange: (mode: CwdMode) => void; selectedProjectPath: string; onSelectedProjectPathChange: (path: string) => void; customCwd: string; onCustomCwdChange: (cwd: string) => void; projects: ProjectPathProject[]; projectsLoading: boolean; projectsError: string | null; fieldError?: string | null; } export const ProjectPathSelector = ({ cwdMode, onCwdModeChange, selectedProjectPath, onSelectedProjectPathChange, customCwd, onCustomCwdChange, projects, projectsLoading, projectsError, fieldError, }: ProjectPathSelectorProps): React.JSX.Element => { const projectOptions = React.useMemo( () => buildProjectPathOptions(projects, selectedProjectPath), [projects, selectedProjectPath] ); return (
{cwdMode === 'project' ? (
( {option.label} )} renderOption={(option, isSelected, query) => ( <>

{renderHighlightedText(option.label, query)}

{renderHighlightedText(option.description ?? '', query)}

)} />
{!selectedProjectPath ? (

Select a project from the list

) : null} {projectsError ?

{projectsError}

: null} {!projectsLoading && projectOptions.length === 0 ? (

No projects found, switch to custom path.

) : null}
) : (
onCustomCwdChange(event.target.value)} placeholder="/absolute/path/to/project" />

If the directory does not exist, it will be created automatically.

)}
{fieldError ? (

{fieldError}

) : null}
); };