@cappasoft/openrouter-model-selector 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # @cappasoft/openrouter-model-selector
2
+
3
+ React UI component for selecting OpenRouter models.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @cappasoft/openrouter-model-selector
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```tsx
14
+ import { useState } from 'react'
15
+ import { ModelSelector } from '@cappasoft/openrouter-model-selector'
16
+ import '@cappasoft/openrouter-model-selector/styles.css'
17
+
18
+ export function App() {
19
+ const [model, setModel] = useState('openai/gpt-4o')
20
+
21
+ return (
22
+ <ModelSelector
23
+ value={model}
24
+ onValueChange={setModel}
25
+ apiKey="sk-or-v1-..."
26
+ />
27
+ )
28
+ }
29
+ ```
30
+
31
+
@@ -0,0 +1,83 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { OpenRouterModel, ModelCategory } from '@cappasoft/openrouter-models';
3
+
4
+ type Locale = "en" | "fr";
5
+ interface Labels {
6
+ placeholder: string;
7
+ loading: string;
8
+ searchPlaceholder: string;
9
+ noResults: string;
10
+ apiKeyRequired: string;
11
+ refreshTitle: string;
12
+ lastUpdatedPrefix: string;
13
+ clearFilters: string;
14
+ libraryTitle: string;
15
+ showAllModelsTitle: string;
16
+ showDetailsTitle: string;
17
+ modelDetailsTitle: string;
18
+ noDescription: string;
19
+ capabilityFast: string;
20
+ capabilityPowerful: string;
21
+ capabilityReasoning: string;
22
+ capabilityCheap: string;
23
+ badgeFast: string;
24
+ badgePowerful: string;
25
+ badgeReasoning: string;
26
+ }
27
+ declare const defaultLabelsEN: Labels;
28
+ declare const defaultLabelsFR: Labels;
29
+ declare function resolveBrowserLocale(): Locale;
30
+ declare function resolveLabels(locale: Locale | undefined, overrides?: Partial<Labels>): Labels;
31
+
32
+ interface UIComponents {
33
+ Button: React.ComponentType<any>;
34
+ Input: React.ComponentType<any>;
35
+ Select: React.ComponentType<any>;
36
+ SelectTrigger: React.ComponentType<any>;
37
+ SelectValue: React.ComponentType<any>;
38
+ SelectContent: React.ComponentType<any>;
39
+ SelectItem: React.ComponentType<any>;
40
+ Dialog: React.ComponentType<any>;
41
+ DialogTrigger: React.ComponentType<any>;
42
+ DialogContent: React.ComponentType<any>;
43
+ DialogHeader: React.ComponentType<any>;
44
+ DialogTitle: React.ComponentType<any>;
45
+ }
46
+ interface ModelSelectorProps {
47
+ value: string;
48
+ onValueChange: (value: string) => void;
49
+ apiKey: string;
50
+ endpoint?: string;
51
+ locale?: Locale;
52
+ labels?: Partial<Labels>;
53
+ components?: Partial<UIComponents>;
54
+ className?: string;
55
+ disabled?: boolean;
56
+ showSearch?: boolean;
57
+ showPricing?: boolean;
58
+ showFilters?: boolean;
59
+ variant?: "default" | "compact";
60
+ showAllInModal?: boolean;
61
+ infoToggle?: boolean;
62
+ }
63
+ declare function ModelSelector({ value, onValueChange, apiKey, endpoint, locale, labels: labelsOverrides, components, className, disabled, showSearch, showPricing, showFilters, variant, showAllInModal, infoToggle, }: ModelSelectorProps): react_jsx_runtime.JSX.Element;
64
+ declare function ModelSelectorCompact({ value, onValueChange, apiKey, endpoint, locale, labels, components, className, disabled, }: Pick<ModelSelectorProps, "value" | "onValueChange" | "apiKey" | "endpoint" | "locale" | "labels" | "components" | "className" | "disabled">): react_jsx_runtime.JSX.Element;
65
+
66
+ interface UseOpenRouterModelsOptions {
67
+ apiKey: string;
68
+ endpoint?: string;
69
+ enabled?: boolean;
70
+ }
71
+ interface UseOpenRouterModelsResult {
72
+ models: OpenRouterModel[];
73
+ categories: ModelCategory[];
74
+ isLoading: boolean;
75
+ isRefreshing: boolean;
76
+ error: string | null;
77
+ lastUpdated: number | null;
78
+ refresh(): void;
79
+ formatPrice(pricePerToken: string): string;
80
+ }
81
+ declare function useOpenRouterModels(options: UseOpenRouterModelsOptions): UseOpenRouterModelsResult;
82
+
83
+ export { type Labels, type Locale, ModelSelector, ModelSelectorCompact, type ModelSelectorProps, type UIComponents, type UseOpenRouterModelsOptions, type UseOpenRouterModelsResult, defaultLabelsEN, defaultLabelsFR, resolveBrowserLocale, resolveLabels, useOpenRouterModels };
package/dist/index.js ADDED
@@ -0,0 +1,790 @@
1
+ import * as React4 from 'react';
2
+ import { useMemo, useState, useCallback, useEffect } from 'react';
3
+ import { X, ChevronDown, ChevronUp, Check, Zap, Sparkles, Brain, DollarSign, Loader2, Settings2, Info, Search } from 'lucide-react';
4
+ import { createOpenRouterModelsClient, RECOMMENDED_MODELS } from '@cappasoft/openrouter-models';
5
+ import { clsx } from 'clsx';
6
+ import { twMerge } from 'tailwind-merge';
7
+ import { Slot } from '@radix-ui/react-slot';
8
+ import { cva } from 'class-variance-authority';
9
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
10
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
11
+ import * as SelectPrimitive from '@radix-ui/react-select';
12
+
13
+ // src/ModelSelector.tsx
14
+ function cn(...inputs) {
15
+ return twMerge(clsx(inputs));
16
+ }
17
+
18
+ // src/i18n.ts
19
+ var defaultLabelsEN = {
20
+ placeholder: "Select a model",
21
+ loading: "Loading...",
22
+ searchPlaceholder: "Search a model...",
23
+ noResults: "No models found",
24
+ apiKeyRequired: "OpenRouter API key required",
25
+ refreshTitle: "Refresh model list",
26
+ lastUpdatedPrefix: "Last updated:",
27
+ clearFilters: "Clear filters",
28
+ libraryTitle: "Model library",
29
+ showAllModelsTitle: "All models",
30
+ showDetailsTitle: "Show details",
31
+ modelDetailsTitle: "Model details",
32
+ noDescription: "No description available.",
33
+ capabilityFast: "Fast",
34
+ capabilityPowerful: "Powerful",
35
+ capabilityReasoning: "Reasoning",
36
+ capabilityCheap: "Cheap",
37
+ badgeFast: "Fast",
38
+ badgePowerful: "Powerful",
39
+ badgeReasoning: "Reasoning"
40
+ };
41
+ var defaultLabelsFR = {
42
+ placeholder: "S\xE9lectionner un mod\xE8le",
43
+ loading: "Chargement...",
44
+ searchPlaceholder: "Rechercher un mod\xE8le...",
45
+ noResults: "Aucun mod\xE8le trouv\xE9",
46
+ apiKeyRequired: "Cl\xE9 API OpenRouter requise",
47
+ refreshTitle: "Rafra\xEEchir la liste des mod\xE8les",
48
+ lastUpdatedPrefix: "Derni\xE8re mise \xE0 jour:",
49
+ clearFilters: "Effacer les filtres",
50
+ libraryTitle: "Biblioth\xE8que de mod\xE8les",
51
+ showAllModelsTitle: "Tous les mod\xE8les",
52
+ showDetailsTitle: "Afficher les d\xE9tails",
53
+ modelDetailsTitle: "D\xE9tails du mod\xE8le",
54
+ noDescription: "Pas de description disponible.",
55
+ capabilityFast: "Rapide",
56
+ capabilityPowerful: "Puissant",
57
+ capabilityReasoning: "Raisonnement",
58
+ capabilityCheap: "\xC9conomique",
59
+ badgeFast: "Rapide",
60
+ badgePowerful: "Puissant",
61
+ badgeReasoning: "Raisonnement"
62
+ };
63
+ function resolveBrowserLocale() {
64
+ try {
65
+ if (typeof navigator === "undefined") return "en";
66
+ const candidates = (navigator.languages?.length ? navigator.languages : [navigator.language]).filter(Boolean);
67
+ const first = candidates[0] ?? "";
68
+ if (first.toLowerCase().startsWith("fr")) return "fr";
69
+ return "en";
70
+ } catch {
71
+ return "en";
72
+ }
73
+ }
74
+ function resolveLabels(locale, overrides) {
75
+ const effectiveLocale = locale ?? resolveBrowserLocale();
76
+ const base = effectiveLocale === "fr" ? defaultLabelsFR : defaultLabelsEN;
77
+ return { ...base, ...overrides ?? {} };
78
+ }
79
+ function formatPriceForUI(pricePerToken) {
80
+ const price = parseFloat(pricePerToken);
81
+ if (price === 0) return "Free";
82
+ const pricePerMillion = price * 1e6;
83
+ if (pricePerMillion < 0.01) return "<$0.01/M";
84
+ return `$${pricePerMillion.toFixed(2)}/M`;
85
+ }
86
+ function useOpenRouterModels(options) {
87
+ const enabled = options.enabled ?? true;
88
+ const client = useMemo(() => {
89
+ if (!enabled) return null;
90
+ return createOpenRouterModelsClient({
91
+ apiKey: options.apiKey,
92
+ endpoint: options.endpoint
93
+ });
94
+ }, [enabled, options.apiKey, options.endpoint]);
95
+ const [models, setModels] = useState([]);
96
+ const [categories, setCategories] = useState([]);
97
+ const [isLoading, setIsLoading] = useState(true);
98
+ const [isRefreshing, setIsRefreshing] = useState(false);
99
+ const [error, setError] = useState(null);
100
+ const [lastUpdated, setLastUpdated] = useState(null);
101
+ const load = useCallback(
102
+ async (force) => {
103
+ if (!enabled || !client) {
104
+ setModels([]);
105
+ setCategories([]);
106
+ setLastUpdated(null);
107
+ setError(null);
108
+ setIsLoading(false);
109
+ setIsRefreshing(false);
110
+ return;
111
+ }
112
+ setError(null);
113
+ if (force) setIsRefreshing(true);
114
+ setIsLoading(true);
115
+ try {
116
+ const res = force ? await client.refresh() : await client.listModels();
117
+ setModels(res.models);
118
+ setCategories(res.categories);
119
+ setLastUpdated(res.lastUpdated);
120
+ } catch (err) {
121
+ setError(err instanceof Error ? err.message : "Failed to fetch models");
122
+ } finally {
123
+ setIsLoading(false);
124
+ setIsRefreshing(false);
125
+ }
126
+ },
127
+ [enabled, client]
128
+ );
129
+ useEffect(() => {
130
+ load(false);
131
+ }, [load]);
132
+ const refresh = useCallback(() => void load(true), [load]);
133
+ return {
134
+ models,
135
+ categories,
136
+ isLoading,
137
+ isRefreshing,
138
+ error,
139
+ lastUpdated,
140
+ refresh,
141
+ formatPrice: formatPriceForUI
142
+ };
143
+ }
144
+ var buttonVariants = cva(
145
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
146
+ {
147
+ variants: {
148
+ variant: {
149
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
150
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
151
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
152
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
153
+ ghost: "hover:bg-accent hover:text-accent-foreground",
154
+ link: "text-primary underline-offset-4 hover:underline"
155
+ },
156
+ size: {
157
+ default: "h-10 px-4 py-2",
158
+ sm: "h-9 rounded-md px-3",
159
+ lg: "h-11 rounded-md px-8",
160
+ icon: "h-10 w-10"
161
+ }
162
+ },
163
+ defaultVariants: {
164
+ variant: "default",
165
+ size: "default"
166
+ }
167
+ }
168
+ );
169
+ var Button = React4.forwardRef(
170
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
171
+ const Comp = asChild ? Slot : "button";
172
+ return /* @__PURE__ */ jsx(Comp, { className: cn(buttonVariants({ variant, size, className })), ref, ...props });
173
+ }
174
+ );
175
+ Button.displayName = "Button";
176
+ var Input = React4.forwardRef(({ className, type, ...props }, ref) => {
177
+ return /* @__PURE__ */ jsx(
178
+ "input",
179
+ {
180
+ type,
181
+ className: cn(
182
+ "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
183
+ className
184
+ ),
185
+ ref,
186
+ ...props
187
+ }
188
+ );
189
+ });
190
+ Input.displayName = "Input";
191
+ var Dialog = DialogPrimitive.Root;
192
+ var DialogTrigger = DialogPrimitive.Trigger;
193
+ var DialogPortal = DialogPrimitive.Portal;
194
+ var DialogOverlay = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
195
+ DialogPrimitive.Overlay,
196
+ {
197
+ ref,
198
+ className: cn(
199
+ "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
200
+ className
201
+ ),
202
+ ...props
203
+ }
204
+ ));
205
+ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
206
+ var DialogContent = React4.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
207
+ /* @__PURE__ */ jsx(DialogOverlay, {}),
208
+ /* @__PURE__ */ jsxs(
209
+ DialogPrimitive.Content,
210
+ {
211
+ ref,
212
+ className: cn(
213
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
214
+ className
215
+ ),
216
+ ...props,
217
+ children: [
218
+ children,
219
+ /* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground", children: [
220
+ /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
221
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
222
+ ] })
223
+ ]
224
+ }
225
+ )
226
+ ] }));
227
+ DialogContent.displayName = DialogPrimitive.Content.displayName;
228
+ var DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props });
229
+ DialogHeader.displayName = "DialogHeader";
230
+ var DialogTitle = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Title, { ref, className: cn("text-lg font-semibold leading-none tracking-tight", className), ...props }));
231
+ DialogTitle.displayName = DialogPrimitive.Title.displayName;
232
+ var Select = SelectPrimitive.Root;
233
+ var SelectValue = SelectPrimitive.Value;
234
+ var SelectTrigger = React4.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
235
+ SelectPrimitive.Trigger,
236
+ {
237
+ ref,
238
+ className: cn(
239
+ "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
240
+ className
241
+ ),
242
+ ...props,
243
+ children: [
244
+ children,
245
+ /* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
246
+ ]
247
+ }
248
+ ));
249
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
250
+ var SelectScrollUpButton = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.ScrollUpButton, { ref, className: cn("flex cursor-default items-center justify-center py-1", className), ...props, children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4" }) }));
251
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
252
+ var SelectScrollDownButton = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
253
+ SelectPrimitive.ScrollDownButton,
254
+ {
255
+ ref,
256
+ className: cn("flex cursor-default items-center justify-center py-1", className),
257
+ ...props,
258
+ children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" })
259
+ }
260
+ ));
261
+ SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
262
+ var SelectContent = React4.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
263
+ SelectPrimitive.Content,
264
+ {
265
+ ref,
266
+ className: cn(
267
+ "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
268
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
269
+ className
270
+ ),
271
+ position,
272
+ ...props,
273
+ children: [
274
+ /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
275
+ /* @__PURE__ */ jsx(
276
+ SelectPrimitive.Viewport,
277
+ {
278
+ className: cn(
279
+ "p-1",
280
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
281
+ ),
282
+ children
283
+ }
284
+ ),
285
+ /* @__PURE__ */ jsx(SelectScrollDownButton, {})
286
+ ]
287
+ }
288
+ ) }));
289
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
290
+ var SelectItem = React4.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
291
+ SelectPrimitive.Item,
292
+ {
293
+ ref,
294
+ className: cn(
295
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
296
+ className
297
+ ),
298
+ ...props,
299
+ children: [
300
+ /* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) }) }),
301
+ /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children })
302
+ ]
303
+ }
304
+ ));
305
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
306
+ function formatContextLength(length) {
307
+ if (length >= 1e6) return `${(length / 1e6).toFixed(1)}M ctx`;
308
+ if (length >= 1e3) return `${Math.round(length / 1e3)}K ctx`;
309
+ return `${length} ctx`;
310
+ }
311
+ function matchesCapability(model, filter) {
312
+ if (!filter) return true;
313
+ const id = model.id.toLowerCase();
314
+ const price = parseFloat(model.pricing.prompt);
315
+ switch (filter) {
316
+ case "fast":
317
+ return id.includes("flash") || id.includes("mini") || id.includes("haiku") || id.includes("small") || id.includes("8b");
318
+ case "powerful":
319
+ return id.includes("opus") || id.includes("large") || id.includes("pro") || id.includes("4o") && !id.includes("mini") || id.includes("70b") || id.includes("sonnet-4");
320
+ case "reasoning":
321
+ return id.includes("o1") || id.includes("r1") || id.includes("reasoning");
322
+ case "cheap":
323
+ return price * 1e6 < 1;
324
+ default:
325
+ return true;
326
+ }
327
+ }
328
+ function matchesProvider(model, filter) {
329
+ if (!filter) return true;
330
+ return model.id.toLowerCase().startsWith(filter);
331
+ }
332
+ function ModelSelector({
333
+ value,
334
+ onValueChange,
335
+ apiKey,
336
+ endpoint,
337
+ locale,
338
+ labels: labelsOverrides,
339
+ components,
340
+ className,
341
+ disabled = false,
342
+ showSearch = true,
343
+ showPricing = true,
344
+ showFilters = true,
345
+ variant = "default",
346
+ showAllInModal = false,
347
+ infoToggle = false
348
+ }) {
349
+ const labels = useMemo(() => resolveLabels(locale, labelsOverrides), [locale, labelsOverrides]);
350
+ const ui = {
351
+ Button: components?.Button ?? Button,
352
+ Input: components?.Input ?? Input,
353
+ Select: components?.Select ?? Select,
354
+ SelectTrigger: components?.SelectTrigger ?? SelectTrigger,
355
+ SelectValue: components?.SelectValue ?? SelectValue,
356
+ SelectContent: components?.SelectContent ?? SelectContent,
357
+ SelectItem: components?.SelectItem ?? SelectItem,
358
+ Dialog: components?.Dialog ?? Dialog,
359
+ DialogTrigger: components?.DialogTrigger ?? DialogTrigger,
360
+ DialogContent: components?.DialogContent ?? DialogContent,
361
+ DialogHeader: components?.DialogHeader ?? DialogHeader,
362
+ DialogTitle: components?.DialogTitle ?? DialogTitle
363
+ };
364
+ const { Button: Button2, Input: Input2, Select: Select2, SelectTrigger: SelectTrigger2, SelectValue: SelectValue2, SelectContent: SelectContent2, SelectItem: SelectItem2, Dialog: Dialog2, DialogContent: DialogContent2, DialogHeader: DialogHeader2, DialogTitle: DialogTitle2, DialogTrigger: DialogTrigger2 } = ui;
365
+ const apiKeyMissing = !apiKey;
366
+ const { categories, isLoading, isRefreshing, error, refresh, lastUpdated, models, formatPrice } = useOpenRouterModels({
367
+ apiKey,
368
+ endpoint,
369
+ enabled: !apiKeyMissing
370
+ });
371
+ const [searchQuery, setSearchQuery] = useState("");
372
+ const [providerFilter, setProviderFilter] = useState(null);
373
+ const [capabilityFilter, setCapabilityFilter] = useState(null);
374
+ const [modalOpen, setModalOpen] = useState(false);
375
+ const [showInfo, setShowInfo] = useState(false);
376
+ const hasActiveFilters = providerFilter !== null || capabilityFilter !== null || searchQuery.trim() !== "";
377
+ const clearFilters = () => {
378
+ setSearchQuery("");
379
+ setProviderFilter(null);
380
+ setCapabilityFilter(null);
381
+ };
382
+ const recommendedModels = useMemo(() => {
383
+ const map = new Map(models.map((m) => [m.id, m]));
384
+ return RECOMMENDED_MODELS.map((id) => map.get(id)).filter(Boolean);
385
+ }, [models]);
386
+ const commonModels = useMemo(() => {
387
+ if (!showAllInModal) return null;
388
+ const list = [...recommendedModels];
389
+ if (value && !list.find((m) => m.id === value)) {
390
+ const selected = models.find((m) => m.id === value);
391
+ if (selected) list.push(selected);
392
+ }
393
+ return list;
394
+ }, [recommendedModels, value, models, showAllInModal]);
395
+ const filteredCategories = useMemo(() => {
396
+ return categories.map((cat) => ({
397
+ ...cat,
398
+ models: cat.models.filter((m) => {
399
+ if (searchQuery.trim()) {
400
+ const query = searchQuery.toLowerCase();
401
+ const matchesSearch = m.name.toLowerCase().includes(query) || m.id.toLowerCase().includes(query) || m.description?.toLowerCase().includes(query);
402
+ if (!matchesSearch) return false;
403
+ }
404
+ if (!matchesProvider(m, providerFilter)) return false;
405
+ if (!matchesCapability(m, capabilityFilter)) return false;
406
+ return true;
407
+ }).sort((a, b) => parseFloat(b.pricing.prompt) - parseFloat(a.pricing.prompt))
408
+ })).filter((cat) => cat.models.length > 0);
409
+ }, [categories, searchQuery, providerFilter, capabilityFilter]);
410
+ const selectedModel = useMemo(() => {
411
+ for (const cat of categories) {
412
+ const model = cat.models.find((m) => m.id === value);
413
+ if (model) return model;
414
+ }
415
+ return null;
416
+ }, [categories, value]);
417
+ const handleRefresh = useCallback(() => refresh(), [refresh]);
418
+ const fmtPrice = useCallback(
419
+ (pricePerToken) => {
420
+ const v = parseFloat(pricePerToken);
421
+ if (v === 0) return locale === "fr" ? "Gratuit" : "Free";
422
+ return formatPrice(pricePerToken);
423
+ },
424
+ [formatPrice, locale]
425
+ );
426
+ const getModelBadge = (model) => {
427
+ const id = model.id.toLowerCase();
428
+ if (id.includes("o1") || id.includes("r1") || id.includes("reasoning")) {
429
+ return { icon: /* @__PURE__ */ jsx(Brain, { className: "h-3 w-3" }), label: labels.badgeReasoning, color: "text-purple-500", bgColor: "bg-purple-500/10" };
430
+ }
431
+ if (id.includes("flash") || id.includes("mini") || id.includes("haiku") || id.includes("small")) {
432
+ return { icon: /* @__PURE__ */ jsx(Zap, { className: "h-3 w-3" }), label: labels.badgeFast, color: "text-yellow-500", bgColor: "bg-yellow-500/10" };
433
+ }
434
+ if (id.includes("opus") || id.includes("large") || id.includes("pro") || id.includes("4o") && !id.includes("mini")) {
435
+ return { icon: /* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3" }), label: labels.badgePowerful, color: "text-blue-500", bgColor: "bg-blue-500/10" };
436
+ }
437
+ return null;
438
+ };
439
+ const PROVIDER_FILTERS = [
440
+ { id: "openai", label: "OpenAI", icon: "\u{1F916}" },
441
+ { id: "anthropic", label: "Claude", icon: "\u{1F3AD}" },
442
+ { id: "google", label: "Gemini", icon: "\u{1F48E}" },
443
+ { id: "meta", label: "Llama", icon: "\u{1F999}" },
444
+ { id: "deepseek", label: "DeepSeek", icon: "\u{1F50D}" }
445
+ ];
446
+ const CAPABILITY_FILTERS = [
447
+ { id: "fast", label: labels.capabilityFast, icon: /* @__PURE__ */ jsx(Zap, { className: "h-3 w-3" }), color: "text-yellow-500 border-yellow-500/50 bg-yellow-500/10" },
448
+ { id: "powerful", label: labels.capabilityPowerful, icon: /* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3" }), color: "text-blue-500 border-blue-500/50 bg-blue-500/10" },
449
+ { id: "reasoning", label: labels.capabilityReasoning, icon: /* @__PURE__ */ jsx(Brain, { className: "h-3 w-3" }), color: "text-purple-500 border-purple-500/50 bg-purple-500/10" },
450
+ { id: "cheap", label: labels.capabilityCheap, icon: /* @__PURE__ */ jsx(DollarSign, { className: "h-3 w-3" }), color: "text-green-500 border-green-500/50 bg-green-500/10" }
451
+ ];
452
+ if (apiKeyMissing) {
453
+ return /* @__PURE__ */ jsx("div", { className: "text-sm text-destructive", children: labels.apiKeyRequired });
454
+ }
455
+ if (error) {
456
+ return /* @__PURE__ */ jsxs("div", { className: "text-sm text-destructive", children: [
457
+ "Error loading models: ",
458
+ error
459
+ ] });
460
+ }
461
+ const renderFullSelector = () => /* @__PURE__ */ jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
462
+ (showSearch || showFilters) && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
463
+ showSearch && /* @__PURE__ */ jsxs("div", { className: "relative", children: [
464
+ /* @__PURE__ */ jsx(Search, { className: "absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }),
465
+ /* @__PURE__ */ jsx(
466
+ Input2,
467
+ {
468
+ placeholder: labels.searchPlaceholder,
469
+ value: searchQuery,
470
+ onChange: (e) => setSearchQuery(e.target.value),
471
+ className: "pl-8 h-8"
472
+ }
473
+ )
474
+ ] }),
475
+ showFilters && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
476
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: PROVIDER_FILTERS.map((filter) => /* @__PURE__ */ jsxs(
477
+ Button2,
478
+ {
479
+ variant: providerFilter === filter.id ? "default" : "outline",
480
+ size: "sm",
481
+ className: cn("h-6 px-2 text-xs", providerFilter === filter.id && "bg-primary text-primary-foreground"),
482
+ onClick: () => setProviderFilter(providerFilter === filter.id ? null : filter.id),
483
+ children: [
484
+ filter.icon && /* @__PURE__ */ jsx("span", { className: "mr-1", children: filter.icon }),
485
+ filter.label
486
+ ]
487
+ },
488
+ filter.id
489
+ )) }),
490
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: CAPABILITY_FILTERS.map((filter) => /* @__PURE__ */ jsxs(
491
+ Button2,
492
+ {
493
+ variant: "outline",
494
+ size: "sm",
495
+ className: cn("h-6 px-2 text-xs border", capabilityFilter === filter.id ? filter.color : "text-muted-foreground"),
496
+ onClick: () => setCapabilityFilter(capabilityFilter === filter.id ? null : filter.id),
497
+ children: [
498
+ filter.icon,
499
+ /* @__PURE__ */ jsx("span", { className: "ml-1", children: filter.label })
500
+ ]
501
+ },
502
+ filter.id
503
+ )) }),
504
+ hasActiveFilters && /* @__PURE__ */ jsxs(
505
+ Button2,
506
+ {
507
+ variant: "ghost",
508
+ size: "sm",
509
+ className: "h-6 px-2 text-xs text-muted-foreground hover:text-foreground",
510
+ onClick: clearFilters,
511
+ children: [
512
+ /* @__PURE__ */ jsx(X, { className: "h-3 w-3 mr-1" }),
513
+ labels.clearFilters
514
+ ]
515
+ }
516
+ )
517
+ ] })
518
+ ] }),
519
+ /* @__PURE__ */ jsx("div", { className: "h-[400px] overflow-y-auto border rounded-md p-2", children: filteredCategories.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 text-center text-sm text-muted-foreground", children: labels.noResults }) : filteredCategories.map((category) => /* @__PURE__ */ jsxs("div", { className: "mb-4 last:mb-0", children: [
520
+ /* @__PURE__ */ jsx("div", { className: "px-2 py-1.5 text-xs font-semibold text-muted-foreground bg-muted/50 mb-1 rounded", children: category.name }),
521
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-1", children: category.models.map((model) => {
522
+ const badge = getModelBadge(model);
523
+ const isSelected = value === model.id;
524
+ return /* @__PURE__ */ jsxs(
525
+ "div",
526
+ {
527
+ className: cn(
528
+ "flex flex-col p-2 rounded-md cursor-pointer transition-colors border",
529
+ isSelected ? "bg-primary/10 border-primary" : "hover:bg-muted border-transparent"
530
+ ),
531
+ onClick: () => {
532
+ onValueChange(model.id);
533
+ if (showAllInModal) setModalOpen(false);
534
+ },
535
+ children: [
536
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
537
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-sm", children: model.name }),
538
+ badge && /* @__PURE__ */ jsxs("span", { className: cn("text-[10px] px-1.5 py-0.5 rounded-full flex items-center gap-1", badge.color, badge.bgColor), children: [
539
+ badge.icon,
540
+ badge.label
541
+ ] })
542
+ ] }),
543
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-[10px] text-muted-foreground mt-1", children: [
544
+ /* @__PURE__ */ jsxs("span", { children: [
545
+ formatContextLength(model.context_length),
546
+ " context"
547
+ ] }),
548
+ showPricing && /* @__PURE__ */ jsx("span", { children: "\u2022" }),
549
+ showPricing && /* @__PURE__ */ jsx("span", { children: fmtPrice(model.pricing.prompt) })
550
+ ] })
551
+ ]
552
+ },
553
+ model.id
554
+ );
555
+ }) })
556
+ ] }, category.name)) })
557
+ ] }) });
558
+ if (showAllInModal) {
559
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", className), children: [
560
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
561
+ /* @__PURE__ */ jsxs(Select2, { value, onValueChange, disabled: disabled || isLoading, children: [
562
+ /* @__PURE__ */ jsx(SelectTrigger2, { className: "flex-1 text-left", children: isLoading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
563
+ /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }),
564
+ /* @__PURE__ */ jsx("span", { children: labels.loading })
565
+ ] }) : /* @__PURE__ */ jsx(SelectValue2, { placeholder: labels.placeholder, children: selectedModel && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
566
+ /* @__PURE__ */ jsx("span", { className: "truncate font-medium", children: selectedModel.name }),
567
+ getModelBadge(selectedModel) && /* @__PURE__ */ jsx(Zap, { className: cn("h-3 w-3 shrink-0", getModelBadge(selectedModel).color.split(" ")[0]) })
568
+ ] }) }) }),
569
+ /* @__PURE__ */ jsxs(SelectContent2, { children: [
570
+ commonModels?.map((model) => /* @__PURE__ */ jsx(SelectItem2, { value: model.id, children: /* @__PURE__ */ jsx("span", { className: "font-medium", children: model.name }) }, model.id)),
571
+ !commonModels?.length && !isLoading && /* @__PURE__ */ jsx("div", { className: "p-2 text-xs text-muted-foreground", children: labels.noResults })
572
+ ] })
573
+ ] }),
574
+ /* @__PURE__ */ jsxs(Dialog2, { open: modalOpen, onOpenChange: setModalOpen, children: [
575
+ /* @__PURE__ */ jsx(DialogTrigger2, { asChild: true, children: /* @__PURE__ */ jsx(Button2, { variant: "outline", size: "icon", className: "shrink-0", title: labels.showAllModelsTitle, children: /* @__PURE__ */ jsx(Settings2, { className: "h-4 w-4" }) }) }),
576
+ /* @__PURE__ */ jsxs(DialogContent2, { className: "max-w-2xl max-h-[85vh]", children: [
577
+ /* @__PURE__ */ jsx(DialogHeader2, { children: /* @__PURE__ */ jsx(DialogTitle2, { children: labels.libraryTitle }) }),
578
+ renderFullSelector()
579
+ ] })
580
+ ] }),
581
+ infoToggle && /* @__PURE__ */ jsx(
582
+ Button2,
583
+ {
584
+ variant: showInfo ? "default" : "outline",
585
+ size: "icon",
586
+ className: "shrink-0",
587
+ onClick: () => setShowInfo(!showInfo),
588
+ title: labels.showDetailsTitle,
589
+ children: /* @__PURE__ */ jsx(Info, { className: "h-4 w-4" })
590
+ }
591
+ )
592
+ ] }),
593
+ infoToggle && showInfo && selectedModel && /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground p-2 bg-muted/30 rounded-md border mt-2 animate-in slide-in-from-top-1 fade-in duration-200", children: [
594
+ /* @__PURE__ */ jsx("div", { className: "font-medium text-foreground mb-1", children: labels.modelDetailsTitle }),
595
+ selectedModel.description ? /* @__PURE__ */ jsx("p", { className: "mb-2", children: selectedModel.description }) : /* @__PURE__ */ jsx("p", { className: "mb-2 italic", children: labels.noDescription }),
596
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2 text-[10px]", children: [
597
+ /* @__PURE__ */ jsxs("span", { className: "bg-background border rounded px-1.5 py-0.5", children: [
598
+ "Context: ",
599
+ formatContextLength(selectedModel.context_length)
600
+ ] }),
601
+ /* @__PURE__ */ jsxs("span", { className: "bg-background border rounded px-1.5 py-0.5", children: [
602
+ "Input: ",
603
+ fmtPrice(selectedModel.pricing.prompt)
604
+ ] }),
605
+ /* @__PURE__ */ jsxs("span", { className: "bg-background border rounded px-1.5 py-0.5", children: [
606
+ "Output: ",
607
+ fmtPrice(selectedModel.pricing.completion)
608
+ ] })
609
+ ] })
610
+ ] })
611
+ ] });
612
+ }
613
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", className), children: [
614
+ /* @__PURE__ */ jsxs(Select2, { value, onValueChange, disabled: disabled || isLoading, children: [
615
+ /* @__PURE__ */ jsx(SelectTrigger2, { className: cn(variant === "compact" && "h-8 text-sm"), children: isLoading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
616
+ /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }),
617
+ /* @__PURE__ */ jsx("span", { children: labels.loading })
618
+ ] }) : /* @__PURE__ */ jsx(SelectValue2, { placeholder: labels.placeholder, children: selectedModel && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
619
+ /* @__PURE__ */ jsx("span", { children: selectedModel.name }),
620
+ getModelBadge(selectedModel) && /* @__PURE__ */ jsx("span", { className: cn("flex items-center gap-0.5 text-xs", getModelBadge(selectedModel).color), children: getModelBadge(selectedModel).icon })
621
+ ] }) }) }),
622
+ /* @__PURE__ */ jsxs(SelectContent2, { className: "max-h-[500px] min-w-[var(--radix-select-trigger-width)] w-full", children: [
623
+ /* @__PURE__ */ jsxs("div", { className: "p-2 border-b space-y-2", children: [
624
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
625
+ /* @__PURE__ */ jsx("div", { className: "grid w-full gap-1 grid-cols-[repeat(auto-fit,minmax(150px,1fr))]", children: recommendedModels.map((model) => /* @__PURE__ */ jsxs(
626
+ Button2,
627
+ {
628
+ size: "sm",
629
+ variant: value === model.id ? "default" : "outline",
630
+ className: "h-8 px-2 text-xs justify-start",
631
+ onClick: (e) => {
632
+ e.stopPropagation();
633
+ onValueChange(model.id);
634
+ },
635
+ children: [
636
+ /* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3 mr-1" }),
637
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: model.name })
638
+ ]
639
+ },
640
+ model.id
641
+ )) }),
642
+ /* @__PURE__ */ jsx(
643
+ Button2,
644
+ {
645
+ size: "icon",
646
+ variant: "ghost",
647
+ className: "h-8 w-8",
648
+ onClick: (e) => {
649
+ e.stopPropagation();
650
+ handleRefresh();
651
+ },
652
+ title: labels.refreshTitle,
653
+ children: isRefreshing ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4" })
654
+ }
655
+ )
656
+ ] }),
657
+ lastUpdated && /* @__PURE__ */ jsxs("div", { className: "text-[11px] text-muted-foreground", children: [
658
+ labels.lastUpdatedPrefix,
659
+ " ",
660
+ new Date(lastUpdated).toLocaleTimeString(locale === "fr" ? "fr-FR" : "en-US")
661
+ ] })
662
+ ] }),
663
+ (showSearch || showFilters) && /* @__PURE__ */ jsxs("div", { className: "p-2 border-b space-y-2", children: [
664
+ showSearch && /* @__PURE__ */ jsxs("div", { className: "relative", children: [
665
+ /* @__PURE__ */ jsx(Search, { className: "absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }),
666
+ /* @__PURE__ */ jsx(
667
+ Input2,
668
+ {
669
+ placeholder: labels.searchPlaceholder,
670
+ value: searchQuery,
671
+ onChange: (e) => setSearchQuery(e.target.value),
672
+ className: "pl-8 h-8",
673
+ onClick: (e) => e.stopPropagation(),
674
+ onKeyDown: (e) => e.stopPropagation()
675
+ }
676
+ )
677
+ ] }),
678
+ showFilters && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
679
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: PROVIDER_FILTERS.map((filter) => /* @__PURE__ */ jsxs(
680
+ Button2,
681
+ {
682
+ variant: providerFilter === filter.id ? "default" : "outline",
683
+ size: "sm",
684
+ className: cn("h-6 px-2 text-xs", providerFilter === filter.id && "bg-primary text-primary-foreground"),
685
+ onClick: (e) => {
686
+ e.stopPropagation();
687
+ setProviderFilter(providerFilter === filter.id ? null : filter.id);
688
+ },
689
+ children: [
690
+ filter.icon && /* @__PURE__ */ jsx("span", { className: "mr-1", children: filter.icon }),
691
+ filter.label
692
+ ]
693
+ },
694
+ filter.id
695
+ )) }),
696
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: CAPABILITY_FILTERS.map((filter) => /* @__PURE__ */ jsxs(
697
+ Button2,
698
+ {
699
+ variant: "outline",
700
+ size: "sm",
701
+ className: cn("h-6 px-2 text-xs border", capabilityFilter === filter.id ? filter.color : "text-muted-foreground"),
702
+ onClick: (e) => {
703
+ e.stopPropagation();
704
+ setCapabilityFilter(capabilityFilter === filter.id ? null : filter.id);
705
+ },
706
+ children: [
707
+ filter.icon,
708
+ /* @__PURE__ */ jsx("span", { className: "ml-1", children: filter.label })
709
+ ]
710
+ },
711
+ filter.id
712
+ )) }),
713
+ hasActiveFilters && /* @__PURE__ */ jsxs(
714
+ Button2,
715
+ {
716
+ variant: "ghost",
717
+ size: "sm",
718
+ className: "h-6 px-2 text-xs text-muted-foreground hover:text-foreground",
719
+ onClick: (e) => {
720
+ e.stopPropagation();
721
+ clearFilters();
722
+ },
723
+ children: [
724
+ /* @__PURE__ */ jsx(X, { className: "h-3 w-3 mr-1" }),
725
+ labels.clearFilters
726
+ ]
727
+ }
728
+ )
729
+ ] })
730
+ ] }),
731
+ /* @__PURE__ */ jsx("div", { className: "max-h-[300px] overflow-y-auto", children: filteredCategories.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 text-center text-sm text-muted-foreground", children: labels.noResults }) : filteredCategories.map((category) => /* @__PURE__ */ jsxs("div", { children: [
732
+ /* @__PURE__ */ jsx("div", { className: "px-2 py-1.5 text-xs font-semibold text-muted-foreground bg-muted/50 sticky top-0", children: category.name }),
733
+ category.models.map((model) => {
734
+ const badge = getModelBadge(model);
735
+ return /* @__PURE__ */ jsx(SelectItem2, { value: model.id, className: "py-2", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
736
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
737
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: model.name }),
738
+ badge && /* @__PURE__ */ jsxs("span", { className: cn("flex items-center gap-0.5 text-xs", badge.color), children: [
739
+ badge.icon,
740
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: badge.label })
741
+ ] })
742
+ ] }),
743
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
744
+ /* @__PURE__ */ jsx("span", { children: formatContextLength(model.context_length) }),
745
+ showPricing && /* @__PURE__ */ jsxs(Fragment, { children: [
746
+ /* @__PURE__ */ jsx("span", { children: "\u2022" }),
747
+ /* @__PURE__ */ jsx("span", { children: fmtPrice(model.pricing.prompt) })
748
+ ] })
749
+ ] })
750
+ ] }) }, model.id);
751
+ })
752
+ ] }, category.name)) })
753
+ ] })
754
+ ] }),
755
+ variant === "default" && selectedModel && /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: selectedModel.description && /* @__PURE__ */ jsx("p", { className: "line-clamp-2", children: selectedModel.description }) })
756
+ ] });
757
+ }
758
+ function ModelSelectorCompact({
759
+ value,
760
+ onValueChange,
761
+ apiKey,
762
+ endpoint,
763
+ locale,
764
+ labels,
765
+ components,
766
+ className,
767
+ disabled = false
768
+ }) {
769
+ return /* @__PURE__ */ jsx(
770
+ ModelSelector,
771
+ {
772
+ value,
773
+ onValueChange,
774
+ apiKey,
775
+ endpoint,
776
+ locale,
777
+ labels,
778
+ components,
779
+ className,
780
+ disabled,
781
+ showSearch: false,
782
+ showPricing: false,
783
+ variant: "compact"
784
+ }
785
+ );
786
+ }
787
+
788
+ export { ModelSelector, ModelSelectorCompact, defaultLabelsEN, defaultLabelsFR, resolveBrowserLocale, resolveLabels, useOpenRouterModels };
789
+ //# sourceMappingURL=index.js.map
790
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils.ts","../src/i18n.ts","../src/useOpenRouterModels.ts","../src/ui/button.tsx","../src/ui/input.tsx","../src/ui/dialog.tsx","../src/ui/select.tsx","../src/ModelSelector.tsx"],"names":["React","React2","jsx","React3","jsxs","useMemo","Button","Input","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Dialog","DialogContent","DialogHeader","DialogTitle","DialogTrigger","useState","useCallback","X"],"mappings":"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","file":"index.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n\n\n","export type Locale = \"en\" | \"fr\"\n\nexport interface Labels {\n placeholder: string\n loading: string\n searchPlaceholder: string\n noResults: string\n apiKeyRequired: string\n refreshTitle: string\n lastUpdatedPrefix: string\n clearFilters: string\n libraryTitle: string\n showAllModelsTitle: string\n showDetailsTitle: string\n modelDetailsTitle: string\n noDescription: string\n\n // Capability labels\n capabilityFast: string\n capabilityPowerful: string\n capabilityReasoning: string\n capabilityCheap: string\n\n // Badge labels\n badgeFast: string\n badgePowerful: string\n badgeReasoning: string\n}\n\nexport const defaultLabelsEN: Labels = {\n placeholder: \"Select a model\",\n loading: \"Loading...\",\n searchPlaceholder: \"Search a model...\",\n noResults: \"No models found\",\n apiKeyRequired: \"OpenRouter API key required\",\n refreshTitle: \"Refresh model list\",\n lastUpdatedPrefix: \"Last updated:\",\n clearFilters: \"Clear filters\",\n libraryTitle: \"Model library\",\n showAllModelsTitle: \"All models\",\n showDetailsTitle: \"Show details\",\n modelDetailsTitle: \"Model details\",\n noDescription: \"No description available.\",\n\n capabilityFast: \"Fast\",\n capabilityPowerful: \"Powerful\",\n capabilityReasoning: \"Reasoning\",\n capabilityCheap: \"Cheap\",\n\n badgeFast: \"Fast\",\n badgePowerful: \"Powerful\",\n badgeReasoning: \"Reasoning\",\n}\n\nexport const defaultLabelsFR: Labels = {\n placeholder: \"Sélectionner un modèle\",\n loading: \"Chargement...\",\n searchPlaceholder: \"Rechercher un modèle...\",\n noResults: \"Aucun modèle trouvé\",\n apiKeyRequired: \"Clé API OpenRouter requise\",\n refreshTitle: \"Rafraîchir la liste des modèles\",\n lastUpdatedPrefix: \"Dernière mise à jour:\",\n clearFilters: \"Effacer les filtres\",\n libraryTitle: \"Bibliothèque de modèles\",\n showAllModelsTitle: \"Tous les modèles\",\n showDetailsTitle: \"Afficher les détails\",\n modelDetailsTitle: \"Détails du modèle\",\n noDescription: \"Pas de description disponible.\",\n\n capabilityFast: \"Rapide\",\n capabilityPowerful: \"Puissant\",\n capabilityReasoning: \"Raisonnement\",\n capabilityCheap: \"Économique\",\n\n badgeFast: \"Rapide\",\n badgePowerful: \"Puissant\",\n badgeReasoning: \"Raisonnement\",\n}\n\nexport function resolveBrowserLocale(): Locale {\n try {\n if (typeof navigator === \"undefined\") return \"en\"\n const candidates = (navigator.languages?.length ? navigator.languages : [navigator.language]).filter(Boolean)\n const first = candidates[0] ?? \"\"\n if (first.toLowerCase().startsWith(\"fr\")) return \"fr\"\n return \"en\"\n } catch {\n return \"en\"\n }\n}\n\nexport function resolveLabels(locale: Locale | undefined, overrides?: Partial<Labels>): Labels {\n const effectiveLocale = locale ?? resolveBrowserLocale()\n const base = effectiveLocale === \"fr\" ? defaultLabelsFR : defaultLabelsEN\n return { ...base, ...(overrides ?? {}) }\n}\n\n\n","import { useCallback, useEffect, useMemo, useState } from \"react\"\n\nimport type { ModelCategory, OpenRouterModel, OpenRouterModelsClient } from \"@cappasoft/openrouter-models\"\nimport { createOpenRouterModelsClient } from \"@cappasoft/openrouter-models\"\n\nexport interface UseOpenRouterModelsOptions {\n apiKey: string\n endpoint?: string\n enabled?: boolean\n}\n\nexport interface UseOpenRouterModelsResult {\n models: OpenRouterModel[]\n categories: ModelCategory[]\n isLoading: boolean\n isRefreshing: boolean\n error: string | null\n lastUpdated: number | null\n refresh(): void\n formatPrice(pricePerToken: string): string\n}\n\nfunction formatPriceForUI(pricePerToken: string): string {\n const price = parseFloat(pricePerToken)\n if (price === 0) return \"Free\"\n const pricePerMillion = price * 1000000\n if (pricePerMillion < 0.01) return \"<$0.01/M\"\n return `$${pricePerMillion.toFixed(2)}/M`\n}\n\nexport function useOpenRouterModels(options: UseOpenRouterModelsOptions): UseOpenRouterModelsResult {\n const enabled = options.enabled ?? true\n\n const client: OpenRouterModelsClient | null = useMemo(() => {\n if (!enabled) return null\n return createOpenRouterModelsClient({\n apiKey: options.apiKey,\n endpoint: options.endpoint,\n })\n }, [enabled, options.apiKey, options.endpoint])\n\n const [models, setModels] = useState<OpenRouterModel[]>([])\n const [categories, setCategories] = useState<ModelCategory[]>([])\n const [isLoading, setIsLoading] = useState(true)\n const [isRefreshing, setIsRefreshing] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [lastUpdated, setLastUpdated] = useState<number | null>(null)\n\n const load = useCallback(\n async (force: boolean) => {\n if (!enabled || !client) {\n setModels([])\n setCategories([])\n setLastUpdated(null)\n setError(null)\n setIsLoading(false)\n setIsRefreshing(false)\n return\n }\n setError(null)\n if (force) setIsRefreshing(true)\n setIsLoading(true)\n try {\n const res = force ? await client.refresh() : await client.listModels()\n setModels(res.models)\n setCategories(res.categories)\n setLastUpdated(res.lastUpdated)\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to fetch models\")\n } finally {\n setIsLoading(false)\n setIsRefreshing(false)\n }\n },\n [enabled, client]\n )\n\n useEffect(() => {\n load(false)\n }, [load])\n\n const refresh = useCallback(() => void load(true), [load])\n\n return {\n models,\n categories,\n isLoading,\n isRefreshing,\n error,\n lastUpdated,\n refresh,\n formatPrice: formatPriceForUI,\n }\n}\n\n\n","import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"../utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive: \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n outline: \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-10 px-4 py-2\",\n sm: \"h-9 rounded-md px-3\",\n lg: \"h-11 rounded-md px-8\",\n icon: \"h-10 w-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {\n asChild?: boolean\n}\n\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />\n }\n)\nButton.displayName = \"Button\"\n\nexport { buttonVariants }\n\n\n","import * as React from \"react\"\n\nimport { cn } from \"../utils\"\n\nexport const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<\"input\">>(({ className, type, ...props }, ref) => {\n return (\n <input\n type={type}\n className={cn(\n \"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n className\n )}\n ref={ref}\n {...props}\n />\n )\n})\nInput.displayName = \"Input\"\n\n\n","import * as React from \"react\"\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\"\nimport { X } from \"lucide-react\"\n\nimport { cn } from \"../utils\"\n\nexport const Dialog = DialogPrimitive.Root\nexport const DialogTrigger = DialogPrimitive.Trigger\nexport const DialogPortal = DialogPrimitive.Portal\nexport const DialogClose = DialogPrimitive.Close\n\nexport const DialogOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\n \"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n />\n))\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName\n\nexport const DialogContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n ref={ref}\n className={cn(\n \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg\",\n className\n )}\n {...props}\n >\n {children}\n <DialogPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\">\n <X className=\"h-4 w-4\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPortal>\n))\nDialogContent.displayName = DialogPrimitive.Content.displayName\n\nexport const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (\n <div className={cn(\"flex flex-col space-y-1.5 text-center sm:text-left\", className)} {...props} />\n)\nDialogHeader.displayName = \"DialogHeader\"\n\nexport const DialogTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title ref={ref} className={cn(\"text-lg font-semibold leading-none tracking-tight\", className)} {...props} />\n))\nDialogTitle.displayName = DialogPrimitive.Title.displayName\n\n\n","import * as React from \"react\"\nimport * as SelectPrimitive from \"@radix-ui/react-select\"\nimport { Check, ChevronDown, ChevronUp } from \"lucide-react\"\n\nimport { cn } from \"../utils\"\n\nexport const Select = SelectPrimitive.Root\nexport const SelectGroup = SelectPrimitive.Group\nexport const SelectValue = SelectPrimitive.Value\n\nexport const SelectTrigger = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Trigger\n ref={ref}\n className={cn(\n \"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1\",\n className\n )}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <ChevronDown className=\"h-4 w-4 opacity-50\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n))\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName\n\nexport const SelectScrollUpButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollUpButton ref={ref} className={cn(\"flex cursor-default items-center justify-center py-1\", className)} {...props}>\n <ChevronUp className=\"h-4 w-4\" />\n </SelectPrimitive.ScrollUpButton>\n))\nSelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName\n\nexport const SelectScrollDownButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollDownButton\n ref={ref}\n className={cn(\"flex cursor-default items-center justify-center py-1\", className)}\n {...props}\n >\n <ChevronDown className=\"h-4 w-4\" />\n </SelectPrimitive.ScrollDownButton>\n))\nSelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName\n\nexport const SelectContent = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>\n>(({ className, children, position = \"popper\", ...props }, ref) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n className={cn(\n \"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n position === \"popper\" &&\n \"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1\",\n className\n )}\n position={position}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n \"p-1\",\n position === \"popper\" &&\n \"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]\"\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n))\nSelectContent.displayName = SelectPrimitive.Content.displayName\n\nexport const SelectItem = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n className\n )}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <Check className=\"h-4 w-4\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n))\nSelectItem.displayName = SelectPrimitive.Item.displayName\n\n\n","import { useCallback, useMemo, useState } from \"react\"\nimport { Brain, DollarSign, Info, Loader2, Search, Settings2, Sparkles, X, Zap } from \"lucide-react\"\n\nimport type { OpenRouterModel } from \"@cappasoft/openrouter-models\"\nimport { RECOMMENDED_MODELS } from \"@cappasoft/openrouter-models\"\n\nimport { cn } from \"./utils\"\nimport type { Labels, Locale } from \"./i18n\"\nimport { resolveLabels } from \"./i18n\"\nimport { useOpenRouterModels } from \"./useOpenRouterModels\"\n\nimport { Button as DefaultButton } from \"./ui/button\"\nimport { Input as DefaultInput } from \"./ui/input\"\nimport { Dialog as DefaultDialog, DialogContent as DefaultDialogContent, DialogHeader as DefaultDialogHeader, DialogTitle as DefaultDialogTitle, DialogTrigger as DefaultDialogTrigger } from \"./ui/dialog\"\nimport {\n Select as DefaultSelect,\n SelectContent as DefaultSelectContent,\n SelectItem as DefaultSelectItem,\n SelectTrigger as DefaultSelectTrigger,\n SelectValue as DefaultSelectValue,\n} from \"./ui/select\"\n\n// Quick filter types\ntype ProviderFilter = \"openai\" | \"anthropic\" | \"google\" | \"meta\" | \"deepseek\" | null\ntype CapabilityFilter = \"fast\" | \"powerful\" | \"reasoning\" | \"cheap\" | null\n\nexport interface UIComponents {\n Button: React.ComponentType<any>\n Input: React.ComponentType<any>\n Select: React.ComponentType<any>\n SelectTrigger: React.ComponentType<any>\n SelectValue: React.ComponentType<any>\n SelectContent: React.ComponentType<any>\n SelectItem: React.ComponentType<any>\n Dialog: React.ComponentType<any>\n DialogTrigger: React.ComponentType<any>\n DialogContent: React.ComponentType<any>\n DialogHeader: React.ComponentType<any>\n DialogTitle: React.ComponentType<any>\n}\n\nexport interface ModelSelectorProps {\n value: string\n onValueChange: (value: string) => void\n\n apiKey: string\n endpoint?: string\n\n locale?: Locale\n labels?: Partial<Labels>\n components?: Partial<UIComponents>\n\n className?: string\n disabled?: boolean\n showSearch?: boolean\n showPricing?: boolean\n showFilters?: boolean\n variant?: \"default\" | \"compact\"\n\n // Advanced layout\n showAllInModal?: boolean\n infoToggle?: boolean\n}\n\nfunction formatContextLength(length: number): string {\n if (length >= 1000000) return `${(length / 1000000).toFixed(1)}M ctx`\n if (length >= 1000) return `${Math.round(length / 1000)}K ctx`\n return `${length} ctx`\n}\n\nfunction matchesCapability(model: OpenRouterModel, filter: CapabilityFilter): boolean {\n if (!filter) return true\n const id = model.id.toLowerCase()\n const price = parseFloat(model.pricing.prompt)\n\n switch (filter) {\n case \"fast\":\n return id.includes(\"flash\") || id.includes(\"mini\") || id.includes(\"haiku\") || id.includes(\"small\") || id.includes(\"8b\")\n case \"powerful\":\n return id.includes(\"opus\") || id.includes(\"large\") || id.includes(\"pro\") || (id.includes(\"4o\") && !id.includes(\"mini\")) || id.includes(\"70b\") || id.includes(\"sonnet-4\")\n case \"reasoning\":\n return id.includes(\"o1\") || id.includes(\"r1\") || id.includes(\"reasoning\")\n case \"cheap\":\n return price * 1000000 < 1\n default:\n return true\n }\n}\n\nfunction matchesProvider(model: OpenRouterModel, filter: ProviderFilter): boolean {\n if (!filter) return true\n return model.id.toLowerCase().startsWith(filter)\n}\n\nexport function ModelSelector({\n value,\n onValueChange,\n apiKey,\n endpoint,\n locale,\n labels: labelsOverrides,\n components,\n className,\n disabled = false,\n showSearch = true,\n showPricing = true,\n showFilters = true,\n variant = \"default\",\n showAllInModal = false,\n infoToggle = false,\n}: ModelSelectorProps) {\n const labels = useMemo(() => resolveLabels(locale, labelsOverrides), [locale, labelsOverrides])\n\n const ui = {\n Button: components?.Button ?? DefaultButton,\n Input: components?.Input ?? DefaultInput,\n Select: components?.Select ?? DefaultSelect,\n SelectTrigger: components?.SelectTrigger ?? DefaultSelectTrigger,\n SelectValue: components?.SelectValue ?? DefaultSelectValue,\n SelectContent: components?.SelectContent ?? DefaultSelectContent,\n SelectItem: components?.SelectItem ?? DefaultSelectItem,\n Dialog: components?.Dialog ?? DefaultDialog,\n DialogTrigger: components?.DialogTrigger ?? DefaultDialogTrigger,\n DialogContent: components?.DialogContent ?? DefaultDialogContent,\n DialogHeader: components?.DialogHeader ?? DefaultDialogHeader,\n DialogTitle: components?.DialogTitle ?? DefaultDialogTitle,\n } satisfies UIComponents\n\n const { Button, Input, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } = ui\n\n const apiKeyMissing = !apiKey\n\n const { categories, isLoading, isRefreshing, error, refresh, lastUpdated, models, formatPrice } = useOpenRouterModels({\n apiKey,\n endpoint,\n enabled: !apiKeyMissing,\n })\n\n const [searchQuery, setSearchQuery] = useState(\"\")\n const [providerFilter, setProviderFilter] = useState<ProviderFilter>(null)\n const [capabilityFilter, setCapabilityFilter] = useState<CapabilityFilter>(null)\n const [modalOpen, setModalOpen] = useState(false)\n const [showInfo, setShowInfo] = useState(false)\n\n const hasActiveFilters = providerFilter !== null || capabilityFilter !== null || searchQuery.trim() !== \"\"\n\n const clearFilters = () => {\n setSearchQuery(\"\")\n setProviderFilter(null)\n setCapabilityFilter(null)\n }\n\n const recommendedModels = useMemo(() => {\n const map = new Map(models.map((m) => [m.id, m]))\n return RECOMMENDED_MODELS.map((id) => map.get(id)).filter(Boolean) as OpenRouterModel[]\n }, [models])\n\n const commonModels = useMemo(() => {\n if (!showAllInModal) return null\n const list = [...recommendedModels]\n if (value && !list.find((m) => m.id === value)) {\n const selected = models.find((m) => m.id === value)\n if (selected) list.push(selected)\n }\n return list\n }, [recommendedModels, value, models, showAllInModal])\n\n const filteredCategories = useMemo(() => {\n return categories\n .map((cat) => ({\n ...cat,\n models: cat.models\n .filter((m) => {\n if (searchQuery.trim()) {\n const query = searchQuery.toLowerCase()\n const matchesSearch =\n m.name.toLowerCase().includes(query) ||\n m.id.toLowerCase().includes(query) ||\n m.description?.toLowerCase().includes(query)\n if (!matchesSearch) return false\n }\n if (!matchesProvider(m, providerFilter)) return false\n if (!matchesCapability(m, capabilityFilter)) return false\n return true\n })\n .sort((a, b) => parseFloat(b.pricing.prompt) - parseFloat(a.pricing.prompt)),\n }))\n .filter((cat) => cat.models.length > 0)\n }, [categories, searchQuery, providerFilter, capabilityFilter])\n\n const selectedModel = useMemo(() => {\n for (const cat of categories) {\n const model = cat.models.find((m) => m.id === value)\n if (model) return model\n }\n return null\n }, [categories, value])\n\n const handleRefresh = useCallback(() => refresh(), [refresh])\n\n const fmtPrice = useCallback(\n (pricePerToken: string) => {\n const v = parseFloat(pricePerToken)\n if (v === 0) return locale === \"fr\" ? \"Gratuit\" : \"Free\"\n return formatPrice(pricePerToken)\n },\n [formatPrice, locale]\n )\n\n const getModelBadge = (model: OpenRouterModel): { icon: React.ReactNode; label: string; color: string; bgColor: string } | null => {\n const id = model.id.toLowerCase()\n if (id.includes(\"o1\") || id.includes(\"r1\") || id.includes(\"reasoning\")) {\n return { icon: <Brain className=\"h-3 w-3\" />, label: labels.badgeReasoning, color: \"text-purple-500\", bgColor: \"bg-purple-500/10\" }\n }\n if (id.includes(\"flash\") || id.includes(\"mini\") || id.includes(\"haiku\") || id.includes(\"small\")) {\n return { icon: <Zap className=\"h-3 w-3\" />, label: labels.badgeFast, color: \"text-yellow-500\", bgColor: \"bg-yellow-500/10\" }\n }\n if (id.includes(\"opus\") || id.includes(\"large\") || id.includes(\"pro\") || (id.includes(\"4o\") && !id.includes(\"mini\"))) {\n return { icon: <Sparkles className=\"h-3 w-3\" />, label: labels.badgePowerful, color: \"text-blue-500\", bgColor: \"bg-blue-500/10\" }\n }\n return null\n }\n\n const PROVIDER_FILTERS: { id: ProviderFilter; label: string; icon?: string }[] = [\n { id: \"openai\", label: \"OpenAI\", icon: \"🤖\" },\n { id: \"anthropic\", label: \"Claude\", icon: \"🎭\" },\n { id: \"google\", label: \"Gemini\", icon: \"💎\" },\n { id: \"meta\", label: \"Llama\", icon: \"🦙\" },\n { id: \"deepseek\", label: \"DeepSeek\", icon: \"🔍\" },\n ]\n\n const CAPABILITY_FILTERS: { id: CapabilityFilter; label: string; icon: React.ReactNode; color: string }[] = [\n { id: \"fast\", label: labels.capabilityFast, icon: <Zap className=\"h-3 w-3\" />, color: \"text-yellow-500 border-yellow-500/50 bg-yellow-500/10\" },\n { id: \"powerful\", label: labels.capabilityPowerful, icon: <Sparkles className=\"h-3 w-3\" />, color: \"text-blue-500 border-blue-500/50 bg-blue-500/10\" },\n { id: \"reasoning\", label: labels.capabilityReasoning, icon: <Brain className=\"h-3 w-3\" />, color: \"text-purple-500 border-purple-500/50 bg-purple-500/10\" },\n { id: \"cheap\", label: labels.capabilityCheap, icon: <DollarSign className=\"h-3 w-3\" />, color: \"text-green-500 border-green-500/50 bg-green-500/10\" },\n ]\n\n if (apiKeyMissing) {\n return <div className=\"text-sm text-destructive\">{labels.apiKeyRequired}</div>\n }\n\n if (error) {\n return <div className=\"text-sm text-destructive\">Error loading models: {error}</div>\n }\n\n const renderFullSelector = () => (\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n {(showSearch || showFilters) && (\n <div className=\"space-y-2\">\n {showSearch && (\n <div className=\"relative\">\n <Search className=\"absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\n <Input\n placeholder={labels.searchPlaceholder}\n value={searchQuery}\n onChange={(e: any) => setSearchQuery(e.target.value)}\n className=\"pl-8 h-8\"\n />\n </div>\n )}\n\n {showFilters && (\n <div className=\"space-y-2\">\n <div className=\"flex flex-wrap gap-1\">\n {PROVIDER_FILTERS.map((filter) => (\n <Button\n key={filter.id}\n variant={providerFilter === filter.id ? \"default\" : \"outline\"}\n size=\"sm\"\n className={cn(\"h-6 px-2 text-xs\", providerFilter === filter.id && \"bg-primary text-primary-foreground\")}\n onClick={() => setProviderFilter(providerFilter === filter.id ? null : filter.id)}\n >\n {filter.icon && <span className=\"mr-1\">{filter.icon}</span>}\n {filter.label}\n </Button>\n ))}\n </div>\n\n <div className=\"flex flex-wrap gap-1\">\n {CAPABILITY_FILTERS.map((filter) => (\n <Button\n key={filter.id}\n variant=\"outline\"\n size=\"sm\"\n className={cn(\"h-6 px-2 text-xs border\", capabilityFilter === filter.id ? filter.color : \"text-muted-foreground\")}\n onClick={() => setCapabilityFilter(capabilityFilter === filter.id ? null : filter.id)}\n >\n {filter.icon}\n <span className=\"ml-1\">{filter.label}</span>\n </Button>\n ))}\n </div>\n\n {hasActiveFilters && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-6 px-2 text-xs text-muted-foreground hover:text-foreground\"\n onClick={clearFilters}\n >\n <X className=\"h-3 w-3 mr-1\" />\n {labels.clearFilters}\n </Button>\n )}\n </div>\n )}\n </div>\n )}\n\n <div className=\"h-[400px] overflow-y-auto border rounded-md p-2\">\n {filteredCategories.length === 0 ? (\n <div className=\"p-4 text-center text-sm text-muted-foreground\">{labels.noResults}</div>\n ) : (\n filteredCategories.map((category) => (\n <div key={category.name} className=\"mb-4 last:mb-0\">\n <div className=\"px-2 py-1.5 text-xs font-semibold text-muted-foreground bg-muted/50 mb-1 rounded\">{category.name}</div>\n <div className=\"grid grid-cols-1 gap-1\">\n {category.models.map((model) => {\n const badge = getModelBadge(model)\n const isSelected = value === model.id\n return (\n <div\n key={model.id}\n className={cn(\n \"flex flex-col p-2 rounded-md cursor-pointer transition-colors border\",\n isSelected ? \"bg-primary/10 border-primary\" : \"hover:bg-muted border-transparent\"\n )}\n onClick={() => {\n onValueChange(model.id)\n if (showAllInModal) setModalOpen(false)\n }}\n >\n <div className=\"flex items-center justify-between\">\n <span className=\"font-medium text-sm\">{model.name}</span>\n {badge && (\n <span className={cn(\"text-[10px] px-1.5 py-0.5 rounded-full flex items-center gap-1\", badge.color, badge.bgColor)}>\n {badge.icon}\n {badge.label}\n </span>\n )}\n </div>\n <div className=\"flex items-center gap-2 text-[10px] text-muted-foreground mt-1\">\n <span>{formatContextLength(model.context_length)} context</span>\n {showPricing && <span>•</span>}\n {showPricing && <span>{fmtPrice(model.pricing.prompt)}</span>}\n </div>\n </div>\n )\n })}\n </div>\n </div>\n ))\n )}\n </div>\n </div>\n </div>\n )\n\n if (showAllInModal) {\n return (\n <div className={cn(\"space-y-2\", className)}>\n <div className=\"flex items-center gap-2\">\n <Select value={value} onValueChange={onValueChange} disabled={disabled || isLoading}>\n <SelectTrigger className=\"flex-1 text-left\">\n {isLoading ? (\n <div className=\"flex items-center gap-2\">\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n <span>{labels.loading}</span>\n </div>\n ) : (\n <SelectValue placeholder={labels.placeholder}>\n {selectedModel && (\n <div className=\"flex items-center gap-2 min-w-0\">\n <span className=\"truncate font-medium\">{selectedModel.name}</span>\n {getModelBadge(selectedModel) && (\n <Zap className={cn(\"h-3 w-3 shrink-0\", getModelBadge(selectedModel)!.color.split(\" \")[0])} />\n )}\n </div>\n )}\n </SelectValue>\n )}\n </SelectTrigger>\n <SelectContent>\n {commonModels?.map((model) => (\n <SelectItem key={model.id} value={model.id}>\n <span className=\"font-medium\">{model.name}</span>\n </SelectItem>\n ))}\n {!commonModels?.length && !isLoading && <div className=\"p-2 text-xs text-muted-foreground\">{labels.noResults}</div>}\n </SelectContent>\n </Select>\n\n <Dialog open={modalOpen} onOpenChange={setModalOpen}>\n <DialogTrigger asChild>\n <Button variant=\"outline\" size=\"icon\" className=\"shrink-0\" title={labels.showAllModelsTitle}>\n <Settings2 className=\"h-4 w-4\" />\n </Button>\n </DialogTrigger>\n <DialogContent className=\"max-w-2xl max-h-[85vh]\">\n <DialogHeader>\n <DialogTitle>{labels.libraryTitle}</DialogTitle>\n </DialogHeader>\n {renderFullSelector()}\n </DialogContent>\n </Dialog>\n\n {infoToggle && (\n <Button\n variant={showInfo ? \"default\" : \"outline\"}\n size=\"icon\"\n className=\"shrink-0\"\n onClick={() => setShowInfo(!showInfo)}\n title={labels.showDetailsTitle}\n >\n <Info className=\"h-4 w-4\" />\n </Button>\n )}\n </div>\n\n {infoToggle && showInfo && selectedModel && (\n <div className=\"text-xs text-muted-foreground p-2 bg-muted/30 rounded-md border mt-2 animate-in slide-in-from-top-1 fade-in duration-200\">\n <div className=\"font-medium text-foreground mb-1\">{labels.modelDetailsTitle}</div>\n {selectedModel.description ? <p className=\"mb-2\">{selectedModel.description}</p> : <p className=\"mb-2 italic\">{labels.noDescription}</p>}\n <div className=\"flex flex-wrap gap-2 text-[10px]\">\n <span className=\"bg-background border rounded px-1.5 py-0.5\">Context: {formatContextLength(selectedModel.context_length)}</span>\n <span className=\"bg-background border rounded px-1.5 py-0.5\">Input: {fmtPrice(selectedModel.pricing.prompt)}</span>\n <span className=\"bg-background border rounded px-1.5 py-0.5\">Output: {fmtPrice(selectedModel.pricing.completion)}</span>\n </div>\n </div>\n )}\n </div>\n )\n }\n\n return (\n <div className={cn(\"space-y-2\", className)}>\n <Select value={value} onValueChange={onValueChange} disabled={disabled || isLoading}>\n <SelectTrigger className={cn(variant === \"compact\" && \"h-8 text-sm\")}>\n {isLoading ? (\n <div className=\"flex items-center gap-2\">\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n <span>{labels.loading}</span>\n </div>\n ) : (\n <SelectValue placeholder={labels.placeholder}>\n {selectedModel && (\n <div className=\"flex items-center gap-2\">\n <span>{selectedModel.name}</span>\n {getModelBadge(selectedModel) && (\n <span className={cn(\"flex items-center gap-0.5 text-xs\", getModelBadge(selectedModel)!.color)}>{getModelBadge(selectedModel)!.icon}</span>\n )}\n </div>\n )}\n </SelectValue>\n )}\n </SelectTrigger>\n <SelectContent className=\"max-h-[500px] min-w-[var(--radix-select-trigger-width)] w-full\">\n <div className=\"p-2 border-b space-y-2\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"grid w-full gap-1 grid-cols-[repeat(auto-fit,minmax(150px,1fr))]\">\n {recommendedModels.map((model) => (\n <Button\n key={model.id}\n size=\"sm\"\n variant={value === model.id ? \"default\" : \"outline\"}\n className=\"h-8 px-2 text-xs justify-start\"\n onClick={(e: any) => {\n e.stopPropagation()\n onValueChange(model.id)\n }}\n >\n <Sparkles className=\"h-3 w-3 mr-1\" />\n <span className=\"truncate\">{model.name}</span>\n </Button>\n ))}\n </div>\n <Button\n size=\"icon\"\n variant=\"ghost\"\n className=\"h-8 w-8\"\n onClick={(e: any) => {\n e.stopPropagation()\n handleRefresh()\n }}\n title={labels.refreshTitle}\n >\n {isRefreshing ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Sparkles className=\"h-4 w-4\" />}\n </Button>\n </div>\n {lastUpdated && (\n <div className=\"text-[11px] text-muted-foreground\">\n {labels.lastUpdatedPrefix}{\" \"}\n {new Date(lastUpdated).toLocaleTimeString(locale === \"fr\" ? \"fr-FR\" : \"en-US\")}\n </div>\n )}\n </div>\n\n {(showSearch || showFilters) && (\n <div className=\"p-2 border-b space-y-2\">\n {showSearch && (\n <div className=\"relative\">\n <Search className=\"absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\n <Input\n placeholder={labels.searchPlaceholder}\n value={searchQuery}\n onChange={(e: any) => setSearchQuery(e.target.value)}\n className=\"pl-8 h-8\"\n onClick={(e: any) => e.stopPropagation()}\n onKeyDown={(e: any) => e.stopPropagation()}\n />\n </div>\n )}\n\n {showFilters && (\n <div className=\"space-y-2\">\n <div className=\"flex flex-wrap gap-1\">\n {PROVIDER_FILTERS.map((filter) => (\n <Button\n key={filter.id}\n variant={providerFilter === filter.id ? \"default\" : \"outline\"}\n size=\"sm\"\n className={cn(\"h-6 px-2 text-xs\", providerFilter === filter.id && \"bg-primary text-primary-foreground\")}\n onClick={(e: any) => {\n e.stopPropagation()\n setProviderFilter(providerFilter === filter.id ? null : filter.id)\n }}\n >\n {filter.icon && <span className=\"mr-1\">{filter.icon}</span>}\n {filter.label}\n </Button>\n ))}\n </div>\n\n <div className=\"flex flex-wrap gap-1\">\n {CAPABILITY_FILTERS.map((filter) => (\n <Button\n key={filter.id}\n variant=\"outline\"\n size=\"sm\"\n className={cn(\"h-6 px-2 text-xs border\", capabilityFilter === filter.id ? filter.color : \"text-muted-foreground\")}\n onClick={(e: any) => {\n e.stopPropagation()\n setCapabilityFilter(capabilityFilter === filter.id ? null : filter.id)\n }}\n >\n {filter.icon}\n <span className=\"ml-1\">{filter.label}</span>\n </Button>\n ))}\n </div>\n\n {hasActiveFilters && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-6 px-2 text-xs text-muted-foreground hover:text-foreground\"\n onClick={(e: any) => {\n e.stopPropagation()\n clearFilters()\n }}\n >\n <X className=\"h-3 w-3 mr-1\" />\n {labels.clearFilters}\n </Button>\n )}\n </div>\n )}\n </div>\n )}\n\n <div className=\"max-h-[300px] overflow-y-auto\">\n {filteredCategories.length === 0 ? (\n <div className=\"p-4 text-center text-sm text-muted-foreground\">{labels.noResults}</div>\n ) : (\n filteredCategories.map((category) => (\n <div key={category.name}>\n <div className=\"px-2 py-1.5 text-xs font-semibold text-muted-foreground bg-muted/50 sticky top-0\">\n {category.name}\n </div>\n {category.models.map((model) => {\n const badge = getModelBadge(model)\n return (\n <SelectItem key={model.id} value={model.id} className=\"py-2\">\n <div className=\"flex flex-col gap-0.5\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium\">{model.name}</span>\n {badge && (\n <span className={cn(\"flex items-center gap-0.5 text-xs\", badge.color)}>\n {badge.icon}\n <span className=\"hidden sm:inline\">{badge.label}</span>\n </span>\n )}\n </div>\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <span>{formatContextLength(model.context_length)}</span>\n {showPricing && (\n <>\n <span>•</span>\n <span>{fmtPrice(model.pricing.prompt)}</span>\n </>\n )}\n </div>\n </div>\n </SelectItem>\n )\n })}\n </div>\n ))\n )}\n </div>\n </SelectContent>\n </Select>\n\n {variant === \"default\" && selectedModel && (\n <div className=\"text-xs text-muted-foreground\">{selectedModel.description && <p className=\"line-clamp-2\">{selectedModel.description}</p>}</div>\n )}\n </div>\n )\n}\n\nexport function ModelSelectorCompact({\n value,\n onValueChange,\n apiKey,\n endpoint,\n locale,\n labels,\n components,\n className,\n disabled = false,\n}: Pick<ModelSelectorProps, \"value\" | \"onValueChange\" | \"apiKey\" | \"endpoint\" | \"locale\" | \"labels\" | \"components\" | \"className\" | \"disabled\">) {\n return (\n <ModelSelector\n value={value}\n onValueChange={onValueChange}\n apiKey={apiKey}\n endpoint={endpoint}\n locale={locale}\n labels={labels}\n components={components}\n className={className}\n disabled={disabled}\n showSearch={false}\n showPricing={false}\n variant=\"compact\"\n />\n )\n}\n\n\n"]}
@@ -0,0 +1 @@
1
+ *,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{--background:0 0% 100%;--foreground:222.2 84% 4.9%;--card:0 0% 100%;--card-foreground:222.2 84% 4.9%;--popover:0 0% 100%;--popover-foreground:222.2 84% 4.9%;--primary:221.2 83.2% 53.3%;--primary-foreground:210 40% 98%;--secondary:210 40% 96.1%;--secondary-foreground:222.2 47.4% 11.2%;--muted:210 40% 96.1%;--muted-foreground:215.4 16.3% 46.9%;--accent:210 40% 96.1%;--accent-foreground:222.2 47.4% 11.2%;--destructive:0 84.2% 60.2%;--destructive-foreground:210 40% 98%;--border:214.3 31.8% 91.4%;--input:214.3 31.8% 91.4%;--ring:221.2 83.2% 53.3%;--radius:0.5rem}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.left-2{left:.5rem}.left-\[50\%\]{left:50%}.right-4{right:1rem}.top-0{top:0}.top-1\/2{top:50%}.top-4{top:1rem}.top-\[50\%\]{top:50%}.z-50{z-index:50}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.ml-1{margin-left:.25rem}.mr-1{margin-right:.25rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[400px\]{height:400px}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.max-h-96{max-height:24rem}.max-h-\[300px\]{max-height:300px}.max-h-\[500px\]{max-height:500px}.max-h-\[85vh\]{max-height:85vh}.w-10{width:2.5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-8{width:2rem}.w-full{width:100%}.min-w-0{min-width:0}.min-w-\[8rem\]{min-width:8rem}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.max-w-2xl{max-width:42rem}.max-w-lg{max-width:32rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.-translate-y-1\/2{--tw-translate-y:-50%}.-translate-y-1\/2,.translate-x-\[-50\%\]{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\[-50\%\]{--tw-translate-x:-50%}.translate-y-\[-50\%\]{--tw-translate-y:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-\[repeat\(auto-fit\2c minmax\(150px\2c 1fr\)\)\]{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-4{gap:1rem}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem*var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-blue-500\/50{border-color:rgba(59,130,246,.5)}.border-green-500\/50{border-color:rgba(34,197,94,.5)}.border-input{border-color:hsl(var(--input))}.border-primary{border-color:hsl(var(--primary))}.border-purple-500\/50{border-color:rgba(168,85,247,.5)}.border-transparent{border-color:transparent}.border-yellow-500\/50{border-color:rgba(234,179,8,.5)}.bg-background{background-color:hsl(var(--background))}.bg-black\/80{background-color:rgba(0,0,0,.8)}.bg-blue-500\/10{background-color:rgba(59,130,246,.1)}.bg-destructive{background-color:hsl(var(--destructive))}.bg-green-500\/10{background-color:rgba(34,197,94,.1)}.bg-muted\/30{background-color:hsl(var(--muted)/.3)}.bg-muted\/50{background-color:hsl(var(--muted)/.5)}.bg-popover{background-color:hsl(var(--popover))}.bg-primary{background-color:hsl(var(--primary))}.bg-primary\/10{background-color:hsl(var(--primary)/.1)}.bg-purple-500\/10{background-color:rgba(168,85,247,.1)}.bg-secondary{background-color:hsl(var(--secondary))}.bg-yellow-500\/10{background-color:rgba(234,179,8,.1)}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.pl-8{padding-left:2rem}.pr-2{padding-right:.5rem}.text-left{text-align:left}.text-center{text-align:center}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.italic{font-style:italic}.leading-none{line-height:1}.tracking-tight{letter-spacing:-.025em}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1))}.text-destructive{color:hsl(var(--destructive))}.text-destructive-foreground{color:hsl(var(--destructive-foreground))}.text-foreground{color:hsl(var(--foreground))}.text-green-500{--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity,1))}.text-muted-foreground{color:hsl(var(--muted-foreground))}.text-popover-foreground{color:hsl(var(--popover-foreground))}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-purple-500{--tw-text-opacity:1;color:rgb(168 85 247/var(--tw-text-opacity,1))}.text-secondary-foreground{color:hsl(var(--secondary-foreground))}.text-yellow-500{--tw-text-opacity:1;color:rgb(234 179 8/var(--tw-text-opacity,1))}.underline-offset-4{text-underline-offset:4px}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring-offset-background{--tw-ring-offset-color:hsl(var(--background))}.\!filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.file\:border-0::file-selector-button{border-width:0}.file\:bg-transparent::file-selector-button{background-color:transparent}.file\:text-sm::file-selector-button{font-size:.875rem;line-height:1.25rem}.file\:font-medium::file-selector-button{font-weight:500}.file\:text-foreground::file-selector-button{color:hsl(var(--foreground))}.placeholder\:text-muted-foreground::-moz-placeholder{color:hsl(var(--muted-foreground))}.placeholder\:text-muted-foreground::placeholder{color:hsl(var(--muted-foreground))}.last\:mb-0:last-child{margin-bottom:0}.hover\:bg-accent:hover{background-color:hsl(var(--accent))}.hover\:bg-destructive\/90:hover{background-color:hsl(var(--destructive)/.9)}.hover\:bg-muted:hover{background-color:hsl(var(--muted))}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary)/.9)}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary)/.8)}.hover\:text-accent-foreground:hover{color:hsl(var(--accent-foreground))}.hover\:text-foreground:hover{color:hsl(var(--foreground))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.focus\:bg-accent:focus{background-color:hsl(var(--accent))}.focus\:text-accent-foreground:focus{color:hsl(var(--accent-foreground))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-ring:focus{--tw-ring-color:hsl(var(--ring))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:hsl(var(--ring))}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y:0.25rem}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom],.data-\[side\=left\]\:-translate-x-1[data-side=left]{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x:-0.25rem}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x:0.25rem}.data-\[side\=right\]\:translate-x-1[data-side=right],.data-\[side\=top\]\:-translate-y-1[data-side=top]{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y:-0.25rem}.data-\[state\=open\]\:bg-accent[data-state=open]{background-color:hsl(var(--accent))}.data-\[state\=open\]\:text-muted-foreground[data-state=open]{color:hsl(var(--muted-foreground))}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}@media (min-width:640px){.sm\:inline{display:inline}.sm\:rounded-lg{border-radius:var(--radius)}.sm\:text-left{text-align:left}}@media (min-width:768px){.md\:text-sm{font-size:.875rem;line-height:1.25rem}}.\[\&\>span\]\:line-clamp-1>span{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:size-4 svg{width:1rem;height:1rem}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@cappasoft/openrouter-model-selector",
3
+ "version": "1.0.0",
4
+ "description": "React UI component for selecting OpenRouter models",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./styles.css": "./dist/styles.css"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsup && npm run build:css",
22
+ "build:css": "tailwindcss -i ./src/styles.css -o ./dist/styles.css --minify",
23
+ "dev": "tsup --watch"
24
+ },
25
+ "keywords": [
26
+ "openrouter",
27
+ "llm",
28
+ "models",
29
+ "react",
30
+ "ui",
31
+ "component"
32
+ ],
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/ecappa/openrouter-model-selector.git",
36
+ "directory": "packages/openrouter-model-selector"
37
+ },
38
+ "license": "MIT",
39
+ "peerDependencies": {
40
+ "react": "^18.0.0",
41
+ "react-dom": "^18.0.0"
42
+ },
43
+ "dependencies": {
44
+ "@cappasoft/openrouter-models": "^1.0.0",
45
+ "@radix-ui/react-dialog": "^1.1.14",
46
+ "@radix-ui/react-select": "^2.2.5",
47
+ "@radix-ui/react-slot": "^1.2.3",
48
+ "class-variance-authority": "^0.7.1",
49
+ "clsx": "^2.1.1",
50
+ "lucide-react": "^0.462.0",
51
+ "tailwind-merge": "^2.6.0"
52
+ },
53
+ "devDependencies": {
54
+ "@types/react": "^18.3.23",
55
+ "@types/react-dom": "^18.3.7",
56
+ "tailwindcss": "^3.4.17",
57
+ "tsup": "^8.3.5",
58
+ "typescript": "^5.8.3"
59
+ }
60
+ }