aboutsummaryrefslogtreecommitdiff
path: root/apps/web/components/model-selector.tsx
blob: f71e7d58848e993fef2ea72991ebba7014d19820 (plain) (blame)
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
"use client"

import { useState } from "react"
import { Button } from "@repo/ui/components/button"
import { ChevronDown } from "lucide-react"
import { motion } from "motion/react"
import { models, type ModelId, ModelIcon } from "@/lib/models"

interface ModelSelectorProps {
	selectedModel?: ModelId
	onModelChange?: (modelId: ModelId) => void
	disabled?: boolean
}

export function ModelSelector({
	selectedModel = "gemini-2.5-pro",
	onModelChange,
	disabled = false,
}: ModelSelectorProps) {
	const [isOpen, setIsOpen] = useState(false)

	const currentModel = models.find((m) => m.id === selectedModel) || models[0]

	const handleModelSelect = (modelId: ModelId) => {
		onModelChange?.(modelId)
		setIsOpen(false)
	}

	return (
		<div className="relative">
			<Button
				type="button"
				variant="ghost"
				className="flex items-center gap-1.5 px-2 py-1.5 rounded-md transition-colors"
				onClick={() => !disabled && setIsOpen(!isOpen)}
				disabled={disabled}
			>
				<ModelIcon width={24} height={24} />
				<span className="text-xs font-medium max-w-32 truncate">
					{currentModel.name}
				</span>
				<motion.div
					animate={{ rotate: isOpen ? 180 : 0 }}
					transition={{ duration: 0.25 }}
				>
					<ChevronDown className="h-3 w-3" />
				</motion.div>
			</Button>

			{isOpen && (
				<>
					<button
						type="button"
						className="fixed inset-0 z-40"
						onClick={() => setIsOpen(false)}
						onKeyDown={(e) => e.key === "Escape" && setIsOpen(false)}
						aria-label="Close model selector"
					/>

					<div className="absolute top-full left-0 mt-1 w-64 bg-background/95 backdrop-blur-xl border border-border rounded-md shadow-xl z-50 overflow-hidden space-y-1">
						<div className="p-1.5 space-y-1">
							{models.map((model) => (
								<button
									key={model.id}
									type="button"
									className={`flex items-center p-1 px-2 rounded-md transition-colors cursor-pointer w-full text-left ${
										selectedModel === model.id
											? "bg-accent"
											: "hover:bg-accent/50"
									}`}
									onClick={() => handleModelSelect(model.id)}
									onKeyDown={(e) =>
										e.key === "Enter" && handleModelSelect(model.id)
									}
								>
									<div className="flex-1 min-w-0">
										<div className="text-sm font-medium text-foreground">
											{model.name}
										</div>
										<div className="text-xs text-muted-foreground truncate">
											{model.description}
										</div>
									</div>
								</button>
							))}
						</div>
					</div>
				</>
			)}
		</div>
	)
}