+ {!isMobile && sidebarScrollPos !== "top" && (
+
+
+
+ )}
+ {isMobile && sidebarScrollPos !== "top" && (
+
+
+
+ )}
{
- const t = e.currentTarget;
- setShowScrollIndicator(
- t.scrollHeight - t.scrollTop >
- t.clientHeight + 10,
- );
- }}
+ onScroll={handleSidebarScroll}
className={cn(
isMobile
- ? "flex flex-row gap-0.5 px-2 py-1.5 overflow-x-auto border-b border-border/40"
+ ? "flex flex-row gap-0.5 px-1 py-1.5 overflow-x-auto [&::-webkit-scrollbar]:h-0 [&::-webkit-scrollbar-track]:bg-transparent"
: "flex flex-col gap-0.5 p-1 overflow-y-auto flex-1 [&::-webkit-scrollbar]:w-0 [&::-webkit-scrollbar-track]:bg-transparent",
)}
+ style={isMobile ? {
+ maskImage: `linear-gradient(to right, ${sidebarScrollPos === "top" ? "black" : "transparent"}, black 24px, black calc(100% - 24px), ${sidebarScrollPos === "bottom" ? "black" : "transparent"})`,
+ WebkitMaskImage: `linear-gradient(to right, ${sidebarScrollPos === "top" ? "black" : "transparent"}, black 24px, black calc(100% - 24px), ${sidebarScrollPos === "bottom" ? "black" : "transparent"})`,
+ } : {
+ maskImage: `linear-gradient(to bottom, ${sidebarScrollPos === "top" ? "black" : "transparent"}, black 32px, black calc(100% - 32px), ${sidebarScrollPos === "bottom" ? "black" : "transparent"})`,
+ WebkitMaskImage: `linear-gradient(to bottom, ${sidebarScrollPos === "top" ? "black" : "transparent"}, black 32px, black calc(100% - 32px), ${sidebarScrollPos === "bottom" ? "black" : "transparent"})`,
+ }}
>
{activeProviders.map((provider, idx) => {
const isAll = provider === "all";
@@ -849,18 +890,23 @@ export function ModelSelector({
)}
{isConfigured
? ` (${count})`
- : " — not configured"}
+ : " (not configured)"}
);
})}
- {!isMobile && showScrollIndicator && (
-
+ {!isMobile && sidebarScrollPos !== "bottom" && (
+
)}
+ {isMobile && sidebarScrollPos !== "bottom" && (
+
+
+
+ )}
);
};
@@ -889,19 +935,26 @@ export function ModelSelector({
key={`${activeTab}-${item.isGlobal ? "g" : "u"}-${config.id}`}
data-model-index={index}
role="option"
+ tabIndex={isMobile ? -1 : 0}
aria-selected={isSelected}
onClick={() => handleSelectItem(item)}
+ onKeyDown={isMobile ? undefined : (e) => {
+ if (e.key === "Enter" || e.key === " ") {
+ e.preventDefault();
+ handleSelectItem(item);
+ }
+ }}
onMouseEnter={() => setFocusedIndex(index)}
className={cn(
- "group flex items-start gap-2.5 px-2.5 py-2 rounded-lg cursor-pointer",
- "transition-all duration-150 mx-1",
- "hover:bg-accent/40 active:scale-[0.99]",
+ "group flex items-center gap-2.5 px-3 py-2 rounded-xl cursor-pointer",
+ "transition-all duration-150 mx-2",
+ "hover:bg-accent/40",
isSelected && "bg-primary/6 dark:bg-primary/8",
- isFocused && "bg-accent/50 ring-1 ring-primary/20",
+ isFocused && "bg-accent/50",
)}
>
{/* Provider icon */}
-
+
{getProviderIcon(config.provider as string, {
isAutoMode,
className: "size-5",
@@ -931,8 +984,8 @@ export function ModelSelector({
{!isAutoMode && hasCitations && (
Citations
@@ -981,7 +1034,7 @@ export function ModelSelector({
: "Add Vision Model";
return (
-
+
{/* Tab header */}
@@ -999,7 +1052,7 @@ export function ModelSelector({
},
{
value: "vision" as const,
- icon: Eye,
+ icon: ScanEye,
label: "Vision",
},
] as const
@@ -1028,7 +1081,7 @@ export function ModelSelector({
"flex",
isMobile
? "flex-col h-[60vh]"
- : "flex-row h-[420px]",
+ : "flex-row h-[380px]",
)}
>
{/* Provider sidebar */}
@@ -1037,33 +1090,30 @@ export function ModelSelector({
{/* Main content */}
{/* Search */}
-
-
+
+
setSearchQuery(e.target.value)
}
- onKeyDown={handleKeyDown}
- autoFocus={!isMobile}
+ onKeyDown={isMobile ? undefined : handleKeyDown}
role="combobox"
aria-expanded={true}
aria-controls="model-selector-list"
className={cn(
- "w-full pl-8 pr-3 py-1.5 text-xs rounded-lg",
- "bg-secondary/30 border border-border/40",
- "focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary/40",
- "placeholder:text-muted-foreground/50",
- "transition-[box-shadow,border-color] duration-200",
+ "w-full pl-8 pr-3 py-2.5 text-sm bg-transparent",
+ "focus:outline-none",
+ "placeholder:text-muted-foreground",
)}
/>
{/* Provider header when filtered */}
{selectedProvider !== "all" && (
-
+
{getProviderIcon(selectedProvider, {
className: "size-4",
})}
@@ -1085,10 +1135,15 @@ export function ModelSelector({
id="model-selector-list"
ref={modelListRef}
role="listbox"
- className="overflow-y-auto flex-1 py-1"
+ className="overflow-y-auto flex-1 py-1 space-y-1 flex flex-col"
+ onScroll={handleModelListScroll}
+ style={{
+ maskImage: `linear-gradient(to bottom, ${modelScrollPos === "top" ? "black" : "transparent"}, black 16px, black calc(100% - 16px), ${modelScrollPos === "bottom" ? "black" : "transparent"})`,
+ WebkitMaskImage: `linear-gradient(to bottom, ${modelScrollPos === "top" ? "black" : "transparent"}, black 16px, black calc(100% - 16px), ${modelScrollPos === "bottom" ? "black" : "transparent"})`,
+ }}
>
{currentDisplayItems.length === 0 ? (
-
+
{selectedProvider !== "all" &&
!configuredProviderSet.has(
selectedProvider,
@@ -1116,22 +1171,21 @@ export function ModelSelector({
{addHandler && (
)}
>
- ) : (
+ ) : searchQuery ? (
<>
-
+
No models found
@@ -1140,13 +1194,22 @@ export function ModelSelector({
term
>
+ ) : (
+ <>
+
+ No models configured
+
+
+ Configure models in your search space settings
+
+ >
)}
) : (
<>
{globalItems.length > 0 && (
<>
-
+
Global Models
{globalItems.map((item, i) =>
@@ -1163,7 +1226,7 @@ export function ModelSelector({
)}
{userItems.length > 0 && (
<>
-
+
Your Configurations
{userItems.map((item, i) =>
@@ -1180,7 +1243,7 @@ export function ModelSelector({
{/* Add model button */}
{addHandler && (
-
+