import { useState } from "react"; import type { DatasetInfo, Preset } from "../types"; import { api } from "../api"; interface SidebarProps { datasets: DatasetInfo[]; presets: Preset[]; setPresets: (p: Preset[]) => void; loading: Record; onAddDataset: (repo: string, config?: string, split?: string, presetId?: string, presetName?: string) => void; onRemoveDataset: (id: string) => void; onToggleDataset: (id: string) => void; onSelectDataset: (id: string) => void; onUpdateDatasetPresetName: (dsId: string, name: string) => void; onClearDatasetPreset: (dsId: string) => void; } export default function Sidebar({ datasets, presets, setPresets, loading, onAddDataset, onRemoveDataset, onToggleDataset, onSelectDataset, onUpdateDatasetPresetName, onClearDatasetPreset, }: SidebarProps) { const [showAddForm, setShowAddForm] = useState(false); const [repo, setRepo] = useState(""); const [config, setConfig] = useState("rlm_call_traces"); const [split, setSplit] = useState("train"); const [presetSearch, setPresetSearch] = useState(""); // Inline preset saving const [savingPresetForId, setSavingPresetForId] = useState(null); const [presetName, setPresetName] = useState(""); // Preset editing panel const [editingDatasetId, setEditingDatasetId] = useState(null); const [editPresetName, setEditPresetName] = useState(""); const handleAdd = () => { if (!repo.trim()) return; onAddDataset(repo.trim(), config, split); setRepo(""); setShowAddForm(false); }; const handleLoadPreset = (p: Preset) => { onAddDataset(p.repo, p.config, p.split || "train", p.id, p.name); }; const handleSavePresetForRepo = async (ds: DatasetInfo) => { if (!presetName.trim()) return; try { const preset = (await api.createPreset({ name: presetName.trim(), repo: ds.repo, config: ds.config, split: ds.split, })) as unknown as Preset; setPresets([...presets, preset]); onUpdateDatasetPresetName(ds.id, presetName.trim()); } catch { /* ignore */ } setPresetName(""); setSavingPresetForId(null); }; const handleUpdatePreset = async (presetId: string, dsId: string) => { if (!editPresetName.trim()) return; try { await api.updatePreset(presetId, { name: editPresetName.trim() }); setPresets( presets.map((p) => (p.id === presetId ? { ...p, name: editPresetName.trim() } : p)) ); onUpdateDatasetPresetName(dsId, editPresetName.trim()); } catch { /* ignore */ } setEditingDatasetId(null); }; const handleDeletePreset = async (id: string, dsId?: string) => { await api.deletePreset(id).catch(() => {}); setPresets(presets.filter((p) => p.id !== id)); if (dsId) { onClearDatasetPreset(dsId); } setEditingDatasetId(null); }; const filteredPresets = presetSearch ? presets.filter( (p) => p.name.toLowerCase().includes(presetSearch.toLowerCase()) || p.repo.toLowerCase().includes(presetSearch.toLowerCase()) ) : presets; return (
{/* Header */}

RLM Eval Visualizer

{/* Presets section */}
Presets
{presets.length === 0 ? (

No presets saved

) : ( <> {presets.length > 6 && ( setPresetSearch(e.target.value)} placeholder="Search presets..." className="w-full px-2 py-1 mb-2 text-xs bg-gray-800 border border-gray-600 rounded text-gray-200 placeholder-gray-500 focus:border-emerald-500 focus:outline-none" /> )}
{filteredPresets.map((p) => (
))}
)}
{/* Loaded Experiments */}
Loaded Datasets
{datasets.length === 0 ? (

No datasets loaded

) : (
{datasets.map((ds) => (
{ if (ds.presetId) { setEditingDatasetId(editingDatasetId === ds.id ? null : ds.id); setEditPresetName(ds.presetName || ""); setShowAddForm(false); } onSelectDataset(ds.id); }} className={`flex items-center gap-2 px-2 py-1.5 rounded text-sm transition-colors cursor-pointer ${ ds.active ? "bg-gray-800" : "bg-gray-900 opacity-60" } ${editingDatasetId === ds.id ? "ring-1 ring-emerald-500" : "hover:bg-gray-800"}`} > onToggleDataset(ds.id)} onClick={(e) => e.stopPropagation()} className="accent-emerald-500 shrink-0" />
{ds.presetName || ds.name}
{ds.metadata.model} | {ds.n_examples} examples
{/* Save as preset bookmark */} {/* Remove */}
{/* Inline preset name input */} {savingPresetForId === ds.id && (
setPresetName(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") handleSavePresetForRepo(ds); if (e.key === "Escape") setSavingPresetForId(null); }} placeholder="Preset name..." className="flex-1 px-2 py-1 text-xs bg-gray-800 border border-gray-600 rounded text-gray-200 placeholder-gray-500 focus:border-emerald-500 focus:outline-none" autoFocus />
)}
))}
)}
{/* Preset edit panel */} {editingDatasetId && (() => { const editDs = datasets.find((d) => d.id === editingDatasetId); if (!editDs?.presetId) return null; return (
Edit Preset
setEditPresetName(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter" && editPresetName.trim()) { handleUpdatePreset(editDs.presetId!, editDs.id); } if (e.key === "Escape") setEditingDatasetId(null); }} placeholder="Preset name..." className="w-full px-2 py-1 text-xs bg-gray-800 border border-gray-600 rounded text-gray-200 placeholder-gray-500 focus:border-emerald-500 focus:outline-none" autoFocus />
); })()} {/* Add Dataset Form */}
{showAddForm ? (
setRepo(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleAdd()} autoFocus />
setConfig(e.target.value)} /> setSplit(e.target.value)} />
) : ( )}
); }