@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 +31 -0
- package/dist/index.d.ts +83 -0
- package/dist/index.js +790 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +1 -0
- package/package.json +60 -0
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
|
+
|
package/dist/index.d.ts
ADDED
|
@@ -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"]}
|
package/dist/styles.css
ADDED
|
@@ -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
|
+
}
|