aboutsummaryrefslogtreecommitdiff
path: root/apps/web/components/new/chat/model-selector.tsx
blob: db77a5c7c72b23d2b4ed0e8be084fb9180060eb8 (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
93
94
95
96
97
98
99
100
101
102
103
"use client"

import { useState } from "react"
import { cn } from "@lib/utils"
import { Button } from "@ui/components/button"
import { dmSansClassName } from "@/lib/fonts"
import { ChevronDownIcon } from "lucide-react"
import { models, type ModelId, modelNames } from "@/lib/models"
import { analytics } from "@/lib/analytics"

interface ChatModelSelectorProps {
	selectedModel?: ModelId
	onModelChange?: (model: ModelId) => void
}

export default function ChatModelSelector({
	selectedModel: selectedModelProp,
	onModelChange,
}: ChatModelSelectorProps = {}) {
	const [internalModel, setInternalModel] = useState<ModelId>("gemini-2.5-pro")
	const [isOpen, setIsOpen] = useState(false)

	const selectedModel = selectedModelProp ?? internalModel
	const currentModelData = modelNames[selectedModel]

	const handleModelSelect = (modelId: ModelId) => {
		if (onModelChange) {
			onModelChange(modelId)
		} else {
			setInternalModel(modelId)
		}
		analytics.modelChanged({ model: modelId })
		setIsOpen(false)
	}

	return (
		<div className="relative flex items-center gap-2">
			<Button
				variant="headers"
				className={cn(
					"rounded-full text-base gap-1 h-10! border-[#73737333] bg-[#0D121A]",
					dmSansClassName(),
				)}
				style={{
					boxShadow: "1.5px 1.5px 4.5px 0 rgba(0, 0, 0, 0.70) inset",
				}}
				onClick={() => setIsOpen(!isOpen)}
			>
				<p className="text-sm">
					{currentModelData.name}{" "}
					<span className="text-[#737373]">{currentModelData.version}</span>
				</p>
				<ChevronDownIcon className="size-4 text-[#737373]" />
			</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-2 w-64 bg-[#0D121A] backdrop-blur-xl border border-[#73737333] rounded-lg shadow-xl z-50 overflow-hidden">
						<div className="p-2 space-y-1">
							{models.map((model) => {
								const modelData = modelNames[model.id]
								return (
									<button
										key={model.id}
										type="button"
										className={cn(
											"flex flex-col items-start p-2 px-3 rounded-md transition-colors cursor-pointer w-full text-left",
											selectedModel === model.id
												? "bg-[#293952]/60"
												: "hover:bg-[#293952]/40",
										)}
										onClick={() => handleModelSelect(model.id)}
										onKeyDown={(e) =>
											e.key === "Enter" && handleModelSelect(model.id)
										}
									>
										<div className="text-sm font-medium text-white">
											{modelData.name}{" "}
											<span className="text-[#737373]">
												{modelData.version}
											</span>
										</div>
										<div className="text-xs text-[#737373] truncate w-full">
											{model.description}
										</div>
									</button>
								)
							})}
						</div>
					</div>
				</>
			)}
		</div>
	)
}