From a1cbd4d92397bc15a9cb627bda5cd360603a2bf5 Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Sat, 20 Dec 2025 19:47:18 -0500 Subject: [PATCH] feat(ui): add dynamic newest models detection for OpenRouter - add created timestamp field to OpenRouterModel type - add getNewestModelsPerProvider() for dynamic newest models - add formatModelAge() for relative time display (e.g., "2d ago") - show newest models section in picker when no search query - sort search results by created date (newest first) --- .../profiles/openrouter-model-picker.tsx | 39 +++++++++++- ui/src/lib/openrouter-types.ts | 1 + ui/src/lib/openrouter-utils.ts | 59 +++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/ui/src/components/profiles/openrouter-model-picker.tsx b/ui/src/components/profiles/openrouter-model-picker.tsx index da985040..ffbdeaa8 100644 --- a/ui/src/components/profiles/openrouter-model-picker.tsx +++ b/ui/src/components/profiles/openrouter-model-picker.tsx @@ -9,12 +9,14 @@ import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Skeleton } from '@/components/ui/skeleton'; -import { Search, RefreshCw, Loader2 } from 'lucide-react'; +import { Search, RefreshCw, Loader2, Sparkles } from 'lucide-react'; import { useOpenRouterCatalog, useRefreshOpenRouterModels } from '@/hooks/use-openrouter-models'; import { searchModels, formatPricingPair, formatContextLength, + formatModelAge, + getNewestModelsPerProvider, CATEGORY_LABELS, } from '@/lib/openrouter-utils'; import type { CategorizedModel, ModelCategory } from '@/lib/openrouter-types'; @@ -46,6 +48,14 @@ export function OpenRouterModelPicker({ }); }, [models, search, selectedCategory]); + // Get newest models for presets (shown when no search) + const newestModels = useMemo(() => { + return getNewestModelsPerProvider(models, 2); + }, [models]); + + // Determine if we should show presets (no search query and no category filter) + const showPresets = !search.trim() && !selectedCategory; + // Group by category const groupedModels = useMemo(() => { const groups: Record = { @@ -159,6 +169,26 @@ export function OpenRouterModelPicker({ ) : (
+ {/* Newest Models Section (shown when no search) */} + {showPresets && newestModels.length > 0 && ( +
+
+ + Newest Models +
+ {newestModels.map((model) => ( + onChange(model.id)} + showAge + /> + ))} +
+ )} + + {/* Category Groups */} {(Object.keys(CATEGORY_LABELS) as ModelCategory[]).map((category) => { const categoryModels = groupedModels[category]; if (categoryModels.length === 0) return null; @@ -190,10 +220,12 @@ function ModelItem({ model, isSelected, onClick, + showAge = false, }: { model: CategorizedModel; isSelected: boolean; onClick: () => void; + showAge?: boolean; }) { return (