import React, { useState } from "react"; import { Plus, ExternalLink } from "lucide-react"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command"; import { DatasetItem } from "@/lib/replayApi"; interface DatasetPickerProps { datasets: DatasetItem[]; loading: boolean; onPickExisting: (item: DatasetItem) => void; onCreateNew: (name: string) => void; onOpenCustom: (repoId: string) => void; children: React.ReactNode; } const REPO_ID_RE = /^[\w.\-]+\/[\w.\-]+$/; const NAME_RE = /^[A-Za-z0-9._-]+$/; const DatasetPicker: React.FC = ({ datasets, loading, onPickExisting, onCreateNew, onOpenCustom, children, }) => { const [open, setOpen] = useState(false); const [query, setQuery] = useState(""); const trimmed = query.trim(); const matchesExisting = datasets.some( (d) => d.repo_id.toLowerCase() === trimmed.toLowerCase(), ); const isRepoId = REPO_ID_RE.test(trimmed); const isName = NAME_RE.test(trimmed) && !trimmed.includes("/"); const canCreate = trimmed.length > 0 && isName && !matchesExisting; const canOpenCustom = isRepoId && !matchesExisting; const createDisabled = matchesExisting || (trimmed !== "" && !canCreate); const createLabel = matchesExisting ? "Already exists" : trimmed === "" ? "Create new dataset…" : canCreate ? `Create "${trimmed}"` : 'Use a name without "/"'; const handleFooterCreate = () => { if (createDisabled) return; onCreateNew(trimmed); reset(); }; const localDatasets = datasets.filter((d) => d.source === "local" || d.source === "both"); const hubDatasets = datasets.filter((d) => d.source === "hub"); const reset = () => { setQuery(""); setOpen(false); }; const handlePick = (item: DatasetItem) => { onPickExisting(item); reset(); }; const handleCreate = () => { if (!canCreate) return; onCreateNew(trimmed); reset(); }; const handleOpenCustom = () => { if (!canOpenCustom) return; onOpenCustom(trimmed); reset(); }; const renderItem = (d: DatasetItem) => ( handlePick(d)} className="text-white aria-selected:bg-gray-700" > {d.repo_id} {d.source === "both" && ( on Hub )} {d.private && ( private )} ); return ( {children} setQuery(v.replace(/[^A-Za-z0-9._\-/]/g, "_"))} onKeyDown={(e) => { if (e.key !== "Enter") return; if (canCreate) { e.preventDefault(); handleCreate(); } else if (canOpenCustom) { e.preventDefault(); handleOpenCustom(); } }} className="text-white" /> {datasets.length === 0 && !canCreate && !canOpenCustom && ( {loading ? "Loading datasets…" : "No datasets yet. Type a name to create one."} )} {localDatasets.length > 0 && ( {localDatasets.map(renderItem)} )} {hubDatasets.length > 0 && ( {hubDatasets.map(renderItem)} )} {canOpenCustom && ( Open "{trimmed}" in viewer )} ); }; export default DatasetPicker;