File size: 3,077 Bytes
a21c316 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { GripVertical, ChevronDown, ChevronRight, Trash2 } from 'lucide-react';
import { cn } from '../../utils/cn';
export interface PreviewModelEntry {
_uid: string;
model: string;
id: string;
index: number;
baseUrl: string;
apiKey: string;
displayName: string;
noImageSupport: boolean;
provider: string;
isAg: boolean;
[key: string]: unknown;
}
export function SortableModelItem({ entry, collapsed, onToggle, onRemove }: {
entry: PreviewModelEntry;
collapsed: boolean;
onToggle: () => void;
onRemove?: () => void;
}) {
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: entry._uid });
const style = {
transform: CSS.Translate.toString(transform),
transition: isDragging ? 'none' : transition,
};
return (
<div ref={setNodeRef} style={style} className={cn(
"rounded-lg border",
isDragging ? "opacity-60 z-50 shadow-lg" : "",
entry.isAg
? "border-orange-200 dark:border-orange-800/40 bg-orange-50/50 dark:bg-orange-900/10"
: "border-gray-200 dark:border-base-300 bg-white dark:bg-base-100"
)}>
<div className="flex items-center gap-1.5 px-2.5 py-1.5">
<button {...attributes} {...listeners} className="cursor-grab active:cursor-grabbing p-0.5 text-gray-300 hover:text-gray-500 dark:text-gray-600 dark:hover:text-gray-400 touch-none">
<GripVertical size={14} />
</button>
<span className="text-[10px] font-mono font-bold text-gray-400 w-5 text-center shrink-0">{entry.index}</span>
<button onClick={onToggle} className="p-0.5 text-gray-400 hover:text-gray-600">
{collapsed ? <ChevronRight size={12} /> : <ChevronDown size={12} />}
</button>
<span className="text-xs font-medium text-gray-800 dark:text-gray-200 flex-1 truncate">{entry.displayName}</span>
{entry.isAg && <img src="/icon.png" alt="AG" className="w-4 h-4 rounded shrink-0" />}
<span className="text-[9px] font-mono text-gray-400 shrink-0 hidden sm:block">{entry.provider}</span>
{onRemove && (
<button onClick={onRemove} className="p-0.5 text-gray-300 hover:text-red-500 transition-colors" title="Remove">
<Trash2 size={12} />
</button>
)}
</div>
{!collapsed && (
<div className="px-3 pb-2 pt-0.5 border-t border-gray-100 dark:border-base-200">
<pre className="text-[9px] font-mono text-gray-500 dark:text-gray-400 leading-relaxed whitespace-pre-wrap">
{JSON.stringify((() => { const { _uid, isAg, ...rest } = entry; return rest; })(), null, 2)}
</pre>
</div>
)}
</div>
);
}
|