diff --git a/apps/desktop/src/app/chat/sidebar/profile-switcher.tsx b/apps/desktop/src/app/chat/sidebar/profile-switcher.tsx index 6a4fb6fcf..bd4ecb617 100644 --- a/apps/desktop/src/app/chat/sidebar/profile-switcher.tsx +++ b/apps/desktop/src/app/chat/sidebar/profile-switcher.tsx @@ -1,3 +1,20 @@ +import { + closestCenter, + DndContext, + type DragEndEvent, + KeyboardSensor, + PointerSensor, + useSensor, + useSensors +} from '@dnd-kit/core' +import { + arrayMove, + horizontalListSortingStrategy, + SortableContext, + sortableKeyboardCoordinates, + useSortable +} from '@dnd-kit/sortable' +import { CSS } from '@dnd-kit/utilities' import { useStore } from '@nanostores/react' import { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' @@ -9,13 +26,16 @@ import { profileColor, profileColorSoft } from '@/lib/profile-color' import { cn } from '@/lib/utils' import { $activeGatewayProfile, + $profileOrder, $profiles, $profileScope, ALL_PROFILES, normalizeProfileKey, refreshActiveProfile, selectProfile, - setShowAllProfiles + setProfileOrder, + setShowAllProfiles, + sortByProfileOrder } from '@/store/profile' import { CreateProfileDialog } from '../../profiles/create-profile-dialog' @@ -29,6 +49,7 @@ export function ProfileRail() { const profiles = useStore($profiles) const scope = useStore($profileScope) const gatewayProfile = useStore($activeGatewayProfile) + const order = useStore($profileOrder) const navigate = useNavigate() const [createOpen, setCreateOpen] = useState(false) @@ -38,7 +59,27 @@ export function ProfileRail() { const defaultProfile = profiles.find(profile => profile.is_default) const onDefault = !isAll && activeKey === 'default' - const named = profiles.filter(profile => !profile.is_default).sort((a, b) => a.name.localeCompare(b.name)) + const named = sortByProfileOrder(profiles.filter(profile => !profile.is_default), order) + + // distance constraint: a small drag reorders, a tap still selects the profile. + const sensors = useSensors( + useSensor(PointerSensor, { activationConstraint: { distance: 4 } }), + useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) + ) + + const handleDragEnd = ({ active, over }: DragEndEvent) => { + if (!over || active.id === over.id) { + return + } + + const ids = named.map(profile => profile.name) + const from = ids.indexOf(String(active.id)) + const to = ids.indexOf(String(over.id)) + + if (from >= 0 && to >= 0) { + setProfileOrder(arrayMove(ids, from, to)) + } + } // Re-pull the running profile + list on mount so a profile created elsewhere // shows up; cheap and best-effort. @@ -64,15 +105,19 @@ export function ProfileRail() { )}